mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-03-12 16:37:13 +01:00
Wiimote to GunCon3
This commit is contained in:
parent
d854ff03fe
commit
cb01548ca2
|
|
@ -401,6 +401,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||||
Io/GameTablet.cpp
|
Io/GameTablet.cpp
|
||||||
Io/GHLtar.cpp
|
Io/GHLtar.cpp
|
||||||
Io/GunCon3.cpp
|
Io/GunCon3.cpp
|
||||||
|
Io/WiimoteManager.cpp
|
||||||
Io/Infinity.cpp
|
Io/Infinity.cpp
|
||||||
Io/interception.cpp
|
Io/interception.cpp
|
||||||
Io/KamenRider.cpp
|
Io/KamenRider.cpp
|
||||||
|
|
@ -652,6 +653,7 @@ target_link_libraries(rpcs3_emu
|
||||||
3rdparty::vulkan
|
3rdparty::vulkan
|
||||||
3rdparty::glew
|
3rdparty::glew
|
||||||
3rdparty::libusb
|
3rdparty::libusb
|
||||||
|
3rdparty::hidapi
|
||||||
3rdparty::wolfssl
|
3rdparty::wolfssl
|
||||||
3rdparty::openal
|
3rdparty::openal
|
||||||
3rdparty::cubeb
|
3rdparty::cubeb
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "GunCon3.h"
|
#include "GunCon3.h"
|
||||||
#include "MouseHandler.h"
|
#include "MouseHandler.h"
|
||||||
|
#include <cmath>
|
||||||
|
#include <climits>
|
||||||
#include "Emu/IdManager.h"
|
#include "Emu/IdManager.h"
|
||||||
#include "Emu/Io/guncon3_config.h"
|
#include "Emu/Io/guncon3_config.h"
|
||||||
#include "Emu/Cell/lv2/sys_usbd.h"
|
#include "Emu/Cell/lv2/sys_usbd.h"
|
||||||
#include "Emu/system_config.h"
|
#include "Emu/system_config.h"
|
||||||
|
#include "WiimoteManager.h"
|
||||||
#include "Input/pad_thread.h"
|
#include "Input/pad_thread.h"
|
||||||
|
#include "Emu/RSX/Overlays/overlay_cursor.h"
|
||||||
|
|
||||||
LOG_CHANNEL(guncon3_log);
|
LOG_CHANNEL(guncon3_log);
|
||||||
|
|
||||||
|
|
@ -127,6 +131,17 @@ usb_device_guncon3::usb_device_guncon3(u32 controller_index, const std::array<u8
|
||||||
: usb_device_emulated(location)
|
: usb_device_emulated(location)
|
||||||
, m_controller_index(controller_index)
|
, m_controller_index(controller_index)
|
||||||
{
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard lock(s_instances_mutex);
|
||||||
|
s_instances.push_back(this);
|
||||||
|
// Sort instances by controller index (P1 < P2 < P3...)
|
||||||
|
// This ensures that the first available GunCon (e.g. at P3) takes the first Wiimote,
|
||||||
|
// and the second (e.g. at P4) takes the second Wiimote.
|
||||||
|
std::sort(s_instances.begin(), s_instances.end(), [](auto* a, auto* b) {
|
||||||
|
return a->m_controller_index < b->m_controller_index;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE,
|
device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE,
|
||||||
UsbDeviceDescriptor {
|
UsbDeviceDescriptor {
|
||||||
.bcdUSB = 0x0110,
|
.bcdUSB = 0x0110,
|
||||||
|
|
@ -174,6 +189,11 @@ usb_device_guncon3::usb_device_guncon3(u32 controller_index, const std::array<u8
|
||||||
|
|
||||||
usb_device_guncon3::~usb_device_guncon3()
|
usb_device_guncon3::~usb_device_guncon3()
|
||||||
{
|
{
|
||||||
|
std::lock_guard lock(s_instances_mutex);
|
||||||
|
if (auto it = std::find(s_instances.begin(), s_instances.end(), this); it != s_instances.end())
|
||||||
|
{
|
||||||
|
s_instances.erase(it);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void usb_device_guncon3::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer)
|
void usb_device_guncon3::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer)
|
||||||
|
|
@ -207,6 +227,88 @@ void usb_device_guncon3::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint,
|
||||||
GunCon3_data gc{};
|
GunCon3_data gc{};
|
||||||
gc.stick_ax = gc.stick_ay = gc.stick_bx = gc.stick_by = 0x7f;
|
gc.stick_ax = gc.stick_ay = gc.stick_bx = gc.stick_by = 0x7f;
|
||||||
|
|
||||||
|
if (auto* wm = WiimoteManager::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;
|
||||||
|
{
|
||||||
|
std::lock_guard lock(s_instances_mutex);
|
||||||
|
auto it = std::lower_bound(s_instances.begin(), s_instances.end(), this, [](auto* a, auto* b) {
|
||||||
|
return a->m_controller_index < b->m_controller_index;
|
||||||
|
});
|
||||||
|
// Since we sort by pointer adress/controller_index in add, and search by this ptr
|
||||||
|
// Actually lower_bound needs a value. std::find is safer for pointer identity.
|
||||||
|
auto found = std::find(s_instances.begin(), s_instances.end(), this);
|
||||||
|
if (found != s_instances.end())
|
||||||
|
{
|
||||||
|
my_wiimote_index = std::distance(s_instances.begin(), found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (my_wiimote_index >= 0 && static_cast<size_t>(my_wiimote_index) < states.size())
|
||||||
|
{
|
||||||
|
const auto& ws = states[my_wiimote_index];
|
||||||
|
|
||||||
|
if (ws.buttons & 0x0400)
|
||||||
|
{
|
||||||
|
gc.btn_trigger = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wiimote to GunCon3 Button Mapping
|
||||||
|
if (ws.buttons & 0x0800) gc.btn_a1 = 1; // Wiimote A -> A1
|
||||||
|
if (ws.buttons & 0x1000) gc.btn_a2 = 1; // Wiimote Minus -> A2
|
||||||
|
if (ws.buttons & 0x0010) gc.btn_c1 = 1; // Wiimote Plus -> C1
|
||||||
|
if (ws.buttons & 0x0200) gc.btn_b1 = 1; // Wiimote 1 -> B1
|
||||||
|
if (ws.buttons & 0x0100) gc.btn_b2 = 1; // Wiimote 2 -> B2
|
||||||
|
if (ws.buttons & 0x8000) gc.btn_b3 = 1; // Wiimote Home -> B3
|
||||||
|
if (ws.buttons & 0x0001) gc.btn_a3 = 1; // Wiimote Left (D-pad) -> A3
|
||||||
|
if (ws.buttons & 0x0002) gc.btn_c2 = 1; // Wiimote Right (D-pad)
|
||||||
|
if (ws.buttons & 0x0008) gc.btn_b1 = 1; // D-pad Up -> B1 (Alt)
|
||||||
|
if (ws.buttons & 0x0004) gc.btn_b2 = 1; // D-pad Down -> B2 (Alt)
|
||||||
|
|
||||||
|
if (ws.ir[0].x < 1023)
|
||||||
|
{
|
||||||
|
// Only use the primary pointer to avoid jumping between multiple IR points
|
||||||
|
s32 raw_x = ws.ir[0].x;
|
||||||
|
s32 raw_y = ws.ir[0].y;
|
||||||
|
|
||||||
|
// Map to GunCon3 range (-32768..32767)
|
||||||
|
// X calculation (Right = 32767, Left = -32768)
|
||||||
|
s32 x_res = 32767 - (raw_x * 65535 / 1023);
|
||||||
|
// Y calculation (Top = 32767, Bottom = -32768)
|
||||||
|
// Swapping to inverted mapping as per user feedback
|
||||||
|
s32 y_res = 32767 - (raw_y * 65535 / 767);
|
||||||
|
|
||||||
|
gc.gun_x = static_cast<int16_t>(std::clamp(x_res, -32768, 32767));
|
||||||
|
gc.gun_y = static_cast<int16_t>(std::clamp(y_res, -32768, 32767));
|
||||||
|
|
||||||
|
// Draw the actual GunCon3 output to the overlay
|
||||||
|
// Mapping GunCon3 range back to virtual_width/height
|
||||||
|
s16 ax = static_cast<s16>((gc.gun_x + 32768) * rsx::overlays::overlay::virtual_width / 65535);
|
||||||
|
s16 ay = static_cast<s16>((32767 - gc.gun_y) * rsx::overlays::overlay::virtual_height / 65535);
|
||||||
|
|
||||||
|
if (g_cfg.io.show_move_cursor)
|
||||||
|
{
|
||||||
|
// Use my_wiimote_index for color/cursor selection (0=Red, 1=Green...)
|
||||||
|
rsx::overlays::set_cursor(rsx::overlays::cursor_offset::cell_gem + my_wiimote_index, ax, ay, { 1.0f, 1.0f, 1.0f, 1.0f }, 100'000, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ws.ir[1].x < 1023)
|
||||||
|
{
|
||||||
|
// Calculate "Z" (distance) based on spread of first two points to emulate depth sensor
|
||||||
|
s32 dx = static_cast<s32>(ws.ir[0].x) - ws.ir[1].x;
|
||||||
|
s32 dy = static_cast<s32>(ws.ir[0].y) - ws.ir[1].y;
|
||||||
|
gc.gun_z = static_cast<int16_t>(std::sqrt(dx * dx + dy * dy));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guncon3_encode(&gc, buf, m_key.data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!is_input_allowed())
|
if (!is_input_allowed())
|
||||||
{
|
{
|
||||||
guncon3_encode(&gc, buf, m_key.data());
|
guncon3_encode(&gc, buf, m_key.data());
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,7 @@ public:
|
||||||
private:
|
private:
|
||||||
u32 m_controller_index;
|
u32 m_controller_index;
|
||||||
std::array<u8, 8> m_key{};
|
std::array<u8, 8> m_key{};
|
||||||
|
|
||||||
|
static inline std::vector<usb_device_guncon3*> s_instances;
|
||||||
|
static inline std::mutex s_instances_mutex;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
259
rpcs3/Emu/Io/WiimoteManager.cpp
Normal file
259
rpcs3/Emu/Io/WiimoteManager.cpp
Normal file
|
|
@ -0,0 +1,259 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "WiimoteManager.h"
|
||||||
|
#include "Emu/System.h"
|
||||||
|
#include "Emu/system_config.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
// Nintendo
|
||||||
|
static constexpr u16 VID_NINTENDO = 0x057e;
|
||||||
|
static constexpr u16 PID_WIIMOTE = 0x0306;
|
||||||
|
static constexpr u16 PID_WIIMOTE_PLUS = 0x0330;
|
||||||
|
|
||||||
|
// Mayflash DolphinBar
|
||||||
|
static constexpr u16 VID_MAYFLASH = 0x0079;
|
||||||
|
static constexpr u16 PID_DOLPHINBAR_START = 0x1800;
|
||||||
|
static constexpr u16 PID_DOLPHINBAR_END = 0x1803;
|
||||||
|
|
||||||
|
WiimoteDevice::WiimoteDevice(hid_device_info* info)
|
||||||
|
: m_path(info->path)
|
||||||
|
, m_serial(info->serial_number ? info->serial_number : L"")
|
||||||
|
{
|
||||||
|
m_handle = hid_open_path(info->path);
|
||||||
|
if (m_handle)
|
||||||
|
{
|
||||||
|
// 1. Connectivity Test (Matching wiimote_test)
|
||||||
|
u8 status_req[] = { 0x15, 0x00 };
|
||||||
|
if (hid_write(m_handle, status_req, sizeof(status_req)) < 0)
|
||||||
|
{
|
||||||
|
hid_close(m_handle);
|
||||||
|
m_handle = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Full Initialization
|
||||||
|
if (initialize_ir())
|
||||||
|
{
|
||||||
|
m_state.connected = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hid_close(m_handle);
|
||||||
|
m_handle = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WiimoteDevice::~WiimoteDevice()
|
||||||
|
{
|
||||||
|
if (m_handle) hid_close(m_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WiimoteDevice::initialize_ir()
|
||||||
|
{
|
||||||
|
auto write_reg = [&](u32 addr, const std::vector<u8>& data) {
|
||||||
|
u8 buf[22] = {0};
|
||||||
|
buf[0] = 0x16; // Write register
|
||||||
|
buf[1] = 0x04;
|
||||||
|
buf[2] = (addr >> 16) & 0xFF;
|
||||||
|
buf[3] = (addr >> 8) & 0xFF;
|
||||||
|
buf[4] = addr & 0xFF;
|
||||||
|
buf[5] = static_cast<u8>(data.size());
|
||||||
|
std::copy(data.begin(), data.end(), &buf[6]);
|
||||||
|
if (hid_write(m_handle, buf, sizeof(buf)) < 0) return false;
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1. Enable IR logic / Pixel Clock
|
||||||
|
u8 ir_on1[] = { 0x13, 0x04 };
|
||||||
|
hid_write(m_handle, ir_on1, 2);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||||
|
u8 ir_on2[] = { 0x1a, 0x04 };
|
||||||
|
hid_write(m_handle, ir_on2, 2);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
|
|
||||||
|
// 2. Enable IR Camera (Matching wiimote_test order)
|
||||||
|
if (!write_reg(0xb00030, {0x08})) return false;
|
||||||
|
|
||||||
|
// 3. Sensitivity Level 3 (Exactly matching wiimote_test)
|
||||||
|
if (!write_reg(0xb00000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x41})) return false;
|
||||||
|
if (!write_reg(0xb0001a, {0x40, 0x00})) return false;
|
||||||
|
|
||||||
|
// 4. IR Mode: Extended (3 bytes per point)
|
||||||
|
if (!write_reg(0xb00033, {0x03})) return false;
|
||||||
|
|
||||||
|
// 5. Reporting mode: Buttons + Accel + IR
|
||||||
|
u8 mode[] = { 0x12, 0x00, 0x33 };
|
||||||
|
if (hid_write(m_handle, mode, sizeof(mode)) < 0) return false;
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WiimoteDevice::update()
|
||||||
|
{
|
||||||
|
if (!m_handle) return false;
|
||||||
|
|
||||||
|
u8 buf[22];
|
||||||
|
int res;
|
||||||
|
|
||||||
|
// Fully drain the buffer until empty to ensure we have the most recent data.
|
||||||
|
// This avoids getting stuck behind a backlog of old reports (e.g. from before IR was enabled).
|
||||||
|
while ((res = hid_read_timeout(m_handle, buf, sizeof(buf), 0)) > 0)
|
||||||
|
{
|
||||||
|
// All data reports (0x30-0x3F) carry buttons in the same location (first 2 bytes).
|
||||||
|
// We mask out accelerometer LSBs (bits 5,6 of both bytes).
|
||||||
|
if ((buf[0] & 0xF0) == 0x30)
|
||||||
|
{
|
||||||
|
m_state.buttons = (buf[1] | (buf[2] << 8)) & 0x9F1F;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mode 0x33: Buttons + Accel + IR (Extended Format)
|
||||||
|
if (buf[0] == 0x33)
|
||||||
|
{
|
||||||
|
// Parse Accelerometer (byte 1: bits 5,6 are X LSBs; byte 2: bit 5 Y LSB, bit 6 Z LSB)
|
||||||
|
m_state.acc_x = (buf[3] << 2) | ((buf[1] >> 5) & 3);
|
||||||
|
m_state.acc_y = (buf[4] << 2) | ((buf[2] >> 5) & 1);
|
||||||
|
m_state.acc_z = (buf[5] << 2) | ((buf[2] >> 6) & 1);
|
||||||
|
|
||||||
|
// Each IR point is 3 bytes in Extended report 0x33.
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
u8* ir = &buf[6 + j * 3];
|
||||||
|
m_state.ir[j].x = (ir[0] | ((ir[2] & 0x30) << 4));
|
||||||
|
m_state.ir[j].y = (ir[1] | ((ir[2] & 0xC0) << 2));
|
||||||
|
m_state.ir[j].size = ir[2] & 0x0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hid_read_timeout returns -1 on error (e.g. device disconnected).
|
||||||
|
if (res < 0) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static WiimoteManager* s_instance = nullptr;
|
||||||
|
|
||||||
|
WiimoteManager::WiimoteManager()
|
||||||
|
{
|
||||||
|
if (!s_instance)
|
||||||
|
s_instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
WiimoteManager::~WiimoteManager()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
if (s_instance == this)
|
||||||
|
s_instance = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
WiimoteManager* WiimoteManager::get_instance()
|
||||||
|
{
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiimoteManager::start()
|
||||||
|
{
|
||||||
|
if (m_running) return;
|
||||||
|
|
||||||
|
// Note: hid_init() is not thread-safe. ideally should be called once at app startup.
|
||||||
|
if (hid_init() != 0) return;
|
||||||
|
|
||||||
|
m_running = true;
|
||||||
|
m_thread = std::thread(&WiimoteManager::thread_proc, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiimoteManager::stop()
|
||||||
|
{
|
||||||
|
m_running = false;
|
||||||
|
if (m_thread.joinable()) m_thread.join();
|
||||||
|
hid_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t WiimoteManager::get_device_count()
|
||||||
|
{
|
||||||
|
std::shared_lock lock(m_mutex);
|
||||||
|
return m_devices.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<WiimoteState> WiimoteManager::get_states()
|
||||||
|
{
|
||||||
|
std::shared_lock lock(m_mutex);
|
||||||
|
std::vector<WiimoteState> states;
|
||||||
|
states.reserve(m_devices.size());
|
||||||
|
|
||||||
|
for (const auto& dev : m_devices)
|
||||||
|
{
|
||||||
|
states.push_back(dev->get_state());
|
||||||
|
}
|
||||||
|
return states;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WiimoteManager::thread_proc()
|
||||||
|
{
|
||||||
|
u32 counter = 0;
|
||||||
|
while (m_running)
|
||||||
|
{
|
||||||
|
// Scan every 2 seconds
|
||||||
|
if (counter++ % 200 == 0)
|
||||||
|
{
|
||||||
|
auto scan_and_add = [&](u16 vid, u16 pid_start, u16 pid_end)
|
||||||
|
{
|
||||||
|
hid_device_info* devs = hid_enumerate(vid, 0);
|
||||||
|
hid_device_info* cur = devs;
|
||||||
|
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
if (cur->product_id >= pid_start && cur->product_id <= pid_end)
|
||||||
|
{
|
||||||
|
bool already_owned = false;
|
||||||
|
{
|
||||||
|
std::shared_lock lock(m_mutex);
|
||||||
|
for (const auto& d : m_devices)
|
||||||
|
{
|
||||||
|
if (d->get_path() == cur->path)
|
||||||
|
{
|
||||||
|
already_owned = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!already_owned)
|
||||||
|
{
|
||||||
|
auto dev = std::make_unique<WiimoteDevice>(cur);
|
||||||
|
if (dev->get_state().connected)
|
||||||
|
{
|
||||||
|
std::unique_lock lock(m_mutex);
|
||||||
|
m_devices.push_back(std::move(dev));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
hid_free_enumeration(devs);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generic Wiimote / DolphinBar Mode 4 (Normal)
|
||||||
|
scan_and_add(VID_NINTENDO, PID_WIIMOTE, PID_WIIMOTE);
|
||||||
|
// Wiimote Plus
|
||||||
|
scan_and_add(VID_NINTENDO, PID_WIIMOTE_PLUS, PID_WIIMOTE_PLUS);
|
||||||
|
// Mayflash DolphinBar Mode 4 (Custom VID/PIDs)
|
||||||
|
// Supports up to 4 players (1800, 1801, 1802, 1803)
|
||||||
|
scan_and_add(VID_MAYFLASH, PID_DOLPHINBAR_START, PID_DOLPHINBAR_END);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update all devices at 100Hz
|
||||||
|
{
|
||||||
|
std::unique_lock lock(m_mutex);
|
||||||
|
m_devices.erase(std::remove_if(m_devices.begin(), m_devices.end(), [](const auto& d)
|
||||||
|
{
|
||||||
|
return !const_cast<WiimoteDevice&>(*d).update();
|
||||||
|
}), m_devices.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
70
rpcs3/Emu/Io/WiimoteManager.h
Normal file
70
rpcs3/Emu/Io/WiimoteManager.h
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "util/types.hpp"
|
||||||
|
#include "Utilities/Thread.h"
|
||||||
|
#include "Utilities/mutex.h"
|
||||||
|
#include <hidapi/hidapi.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
#include <shared_mutex>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
struct WiimoteIRPoint
|
||||||
|
{
|
||||||
|
u16 x = 1023;
|
||||||
|
u16 y = 1023;
|
||||||
|
u8 size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WiimoteState
|
||||||
|
{
|
||||||
|
u16 buttons = 0;
|
||||||
|
s16 acc_x = 0, acc_y = 0, acc_z = 0;
|
||||||
|
WiimoteIRPoint ir[4];
|
||||||
|
bool connected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WiimoteDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WiimoteDevice(hid_device_info* info);
|
||||||
|
~WiimoteDevice();
|
||||||
|
|
||||||
|
bool update();
|
||||||
|
const WiimoteState& get_state() const { return m_state; }
|
||||||
|
std::string get_path() const { return m_path; }
|
||||||
|
std::wstring get_serial() const { return m_serial; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
hid_device* m_handle = nullptr;
|
||||||
|
std::string m_path;
|
||||||
|
std::wstring m_serial;
|
||||||
|
WiimoteState m_state;
|
||||||
|
|
||||||
|
bool initialize_ir();
|
||||||
|
};
|
||||||
|
|
||||||
|
class WiimoteManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WiimoteManager();
|
||||||
|
~WiimoteManager();
|
||||||
|
|
||||||
|
static WiimoteManager* get_instance();
|
||||||
|
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
std::vector<WiimoteState> get_states();
|
||||||
|
size_t get_device_count();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::thread m_thread;
|
||||||
|
atomic_t<bool> m_running{false};
|
||||||
|
std::vector<std::unique_ptr<WiimoteDevice>> m_devices;
|
||||||
|
shared_mutex m_mutex;
|
||||||
|
|
||||||
|
void thread_proc();
|
||||||
|
};
|
||||||
|
|
@ -458,6 +458,18 @@ struct AnalogSensor
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ir_point
|
||||||
|
{
|
||||||
|
u16 x = 1023;
|
||||||
|
u16 y = 1023;
|
||||||
|
u16 size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ir_data
|
||||||
|
{
|
||||||
|
std::array<ir_point, 4> points;
|
||||||
|
};
|
||||||
|
|
||||||
struct VibrateMotor
|
struct VibrateMotor
|
||||||
{
|
{
|
||||||
bool is_large_motor = false;
|
bool is_large_motor = false;
|
||||||
|
|
@ -519,6 +531,7 @@ struct Pad
|
||||||
|
|
||||||
std::vector<Button> m_buttons_external;
|
std::vector<Button> m_buttons_external;
|
||||||
std::array<AnalogStick, 4> m_sticks_external{};
|
std::array<AnalogStick, 4> m_sticks_external{};
|
||||||
|
ir_data m_ir{};
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Pad>> copilots;
|
std::vector<std::shared_ptr<Pad>> copilots;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ namespace rsx
|
||||||
enum cursor_offset : u32
|
enum cursor_offset : u32
|
||||||
{
|
{
|
||||||
cell_gem = 0, // CELL_GEM_MAX_NUM = 4 Move controllers
|
cell_gem = 0, // CELL_GEM_MAX_NUM = 4 Move controllers
|
||||||
last = 4
|
wiimote_ir = 4, // 4 points per Wiimote * 4 Wiimotes (up to 16 points)
|
||||||
|
last = 20
|
||||||
};
|
};
|
||||||
|
|
||||||
class cursor_item
|
class cursor_item
|
||||||
|
|
|
||||||
|
|
@ -276,6 +276,7 @@ struct cfg_root : cfg::node
|
||||||
cfg::_bool keep_pads_connected{this, "Keep pads connected", false, true};
|
cfg::_bool keep_pads_connected{this, "Keep pads connected", false, true};
|
||||||
cfg::uint<0, 100'000> pad_sleep{this, "Pad handler sleep (microseconds)", 1'000, true};
|
cfg::uint<0, 100'000> pad_sleep{this, "Pad handler sleep (microseconds)", 1'000, true};
|
||||||
cfg::_bool background_input_enabled{this, "Background input enabled", true, true};
|
cfg::_bool background_input_enabled{this, "Background input enabled", true, true};
|
||||||
|
cfg::_bool wiimote_continuous_scanning{this, "Wiimote continuous scanning", false, true};
|
||||||
cfg::_bool show_move_cursor{this, "Show move cursor", false, true};
|
cfg::_bool show_move_cursor{this, "Show move cursor", false, true};
|
||||||
cfg::_bool paint_move_spheres{this, "Paint move spheres", false, true};
|
cfg::_bool paint_move_spheres{this, "Paint move spheres", false, true};
|
||||||
cfg::_bool allow_move_hue_set_by_game{this, "Allow move hue set by game", false, true};
|
cfg::_bool allow_move_hue_set_by_game{this, "Allow move hue set by game", false, true};
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ add_library(rpcs3_ui STATIC
|
||||||
breakpoint_list.cpp
|
breakpoint_list.cpp
|
||||||
call_stack_list.cpp
|
call_stack_list.cpp
|
||||||
camera_settings_dialog.cpp
|
camera_settings_dialog.cpp
|
||||||
|
wiimote_settings_dialog.cpp
|
||||||
cg_disasm_window.cpp
|
cg_disasm_window.cpp
|
||||||
cheat_manager.cpp
|
cheat_manager.cpp
|
||||||
clans_settings_dialog.cpp
|
clans_settings_dialog.cpp
|
||||||
|
|
@ -125,6 +126,7 @@ add_library(rpcs3_ui STATIC
|
||||||
|
|
||||||
about_dialog.ui
|
about_dialog.ui
|
||||||
camera_settings_dialog.ui
|
camera_settings_dialog.ui
|
||||||
|
wiimote_settings_dialog.ui
|
||||||
main_window.ui
|
main_window.ui
|
||||||
music_player_dialog.ui
|
music_player_dialog.ui
|
||||||
pad_led_settings_dialog.ui
|
pad_led_settings_dialog.ui
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,7 @@ enum class emu_settings_type
|
||||||
CameraFlip,
|
CameraFlip,
|
||||||
CameraID,
|
CameraID,
|
||||||
Move,
|
Move,
|
||||||
|
WiimoteScan,
|
||||||
Buzz,
|
Buzz,
|
||||||
Turntable,
|
Turntable,
|
||||||
GHLtar,
|
GHLtar,
|
||||||
|
|
@ -368,6 +369,7 @@ inline static const std::map<emu_settings_type, cfg_location> settings_location
|
||||||
{ emu_settings_type::LockOvlIptToP1, { "Input/Output", "Lock overlay input to player one"}},
|
{ emu_settings_type::LockOvlIptToP1, { "Input/Output", "Lock overlay input to player one"}},
|
||||||
{ emu_settings_type::PadHandlerMode, { "Input/Output", "Pad handler mode"}},
|
{ emu_settings_type::PadHandlerMode, { "Input/Output", "Pad handler mode"}},
|
||||||
{ emu_settings_type::PadConnection, { "Input/Output", "Keep pads connected" }},
|
{ emu_settings_type::PadConnection, { "Input/Output", "Keep pads connected" }},
|
||||||
|
{ emu_settings_type::WiimoteScan, { "Input/Output", "Wiimote continuous scanning" }},
|
||||||
{ emu_settings_type::KeyboardHandler, { "Input/Output", "Keyboard"}},
|
{ emu_settings_type::KeyboardHandler, { "Input/Output", "Keyboard"}},
|
||||||
{ emu_settings_type::MouseHandler, { "Input/Output", "Mouse"}},
|
{ emu_settings_type::MouseHandler, { "Input/Output", "Mouse"}},
|
||||||
{ emu_settings_type::Camera, { "Input/Output", "Camera"}},
|
{ emu_settings_type::Camera, { "Input/Output", "Camera"}},
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,8 @@ bool gui_application::Init()
|
||||||
}
|
}
|
||||||
|
|
||||||
m_emu_settings = std::make_shared<emu_settings>();
|
m_emu_settings = std::make_shared<emu_settings>();
|
||||||
|
m_wiimote_manager = std::make_unique<WiimoteManager>();
|
||||||
|
m_wiimote_manager->start();
|
||||||
m_gui_settings = std::make_shared<gui_settings>();
|
m_gui_settings = std::make_shared<gui_settings>();
|
||||||
m_persistent_settings = std::make_shared<persistent_settings>();
|
m_persistent_settings = std::make_shared<persistent_settings>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,8 @@ extern std::unique_ptr<raw_mouse_handler> g_raw_mouse_handler; // Only used for
|
||||||
/** RPCS3 GUI Application Class
|
/** RPCS3 GUI Application Class
|
||||||
* The main point of this class is to do application initialization, to hold the main and game windows and to initialize callbacks.
|
* The main point of this class is to do application initialization, to hold the main and game windows and to initialize callbacks.
|
||||||
*/
|
*/
|
||||||
|
#include "Emu/Io/WiimoteManager.h"
|
||||||
|
|
||||||
class gui_application : public QApplication, public main_application
|
class gui_application : public QApplication, public main_application
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
@ -111,6 +113,7 @@ private:
|
||||||
|
|
||||||
std::deque<std::unique_ptr<QSoundEffect>> m_sound_effects{};
|
std::deque<std::unique_ptr<QSoundEffect>> m_sound_effects{};
|
||||||
|
|
||||||
|
std::unique_ptr<WiimoteManager> m_wiimote_manager;
|
||||||
std::shared_ptr<emu_settings> m_emu_settings;
|
std::shared_ptr<emu_settings> m_emu_settings;
|
||||||
std::shared_ptr<gui_settings> m_gui_settings;
|
std::shared_ptr<gui_settings> m_gui_settings;
|
||||||
std::shared_ptr<persistent_settings> m_persistent_settings;
|
std::shared_ptr<persistent_settings> m_persistent_settings;
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
#include "gui_settings.h"
|
#include "gui_settings.h"
|
||||||
#include "input_dialog.h"
|
#include "input_dialog.h"
|
||||||
#include "camera_settings_dialog.h"
|
#include "camera_settings_dialog.h"
|
||||||
|
#include "wiimote_settings_dialog.h"
|
||||||
#include "ps_move_tracker_dialog.h"
|
#include "ps_move_tracker_dialog.h"
|
||||||
#include "ipc_settings_dialog.h"
|
#include "ipc_settings_dialog.h"
|
||||||
#include "shortcut_utils.h"
|
#include "shortcut_utils.h"
|
||||||
|
|
@ -2992,6 +2993,12 @@ void main_window::CreateConnects()
|
||||||
dlg->open();
|
dlg->open();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(ui->confWiimoteAct, &QAction::triggered, this, [this]()
|
||||||
|
{
|
||||||
|
wiimote_settings_dialog* dlg = new wiimote_settings_dialog(this);
|
||||||
|
dlg->show();
|
||||||
|
});
|
||||||
|
|
||||||
const auto open_rpcn_settings = [this]
|
const auto open_rpcn_settings = [this]
|
||||||
{
|
{
|
||||||
rpcn_settings_dialog dlg(this);
|
rpcn_settings_dialog dlg(this);
|
||||||
|
|
|
||||||
|
|
@ -256,6 +256,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
<addaction name="confPadsAct"/>
|
<addaction name="confPadsAct"/>
|
||||||
<addaction name="menuMice"/>
|
<addaction name="menuMice"/>
|
||||||
|
<addaction name="confWiimoteAct"/>
|
||||||
<addaction name="confCamerasAct"/>
|
<addaction name="confCamerasAct"/>
|
||||||
<addaction name="actionPS_Move_Tracker"/>
|
<addaction name="actionPS_Move_Tracker"/>
|
||||||
</widget>
|
</widget>
|
||||||
|
|
@ -1425,6 +1426,11 @@
|
||||||
<string>GunCon 3</string>
|
<string>GunCon 3</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="confWiimoteAct">
|
||||||
|
<property name="text">
|
||||||
|
<string>Wiimotes</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="confTopShotEliteAct">
|
<action name="confTopShotEliteAct">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Top Shot Elite</string>
|
<string>Top Shot Elite</string>
|
||||||
|
|
|
||||||
|
|
@ -1220,6 +1220,9 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
|
||||||
m_emu_settings->EnhanceCheckBox(ui->padConnectionBox, emu_settings_type::PadConnection);
|
m_emu_settings->EnhanceCheckBox(ui->padConnectionBox, emu_settings_type::PadConnection);
|
||||||
SubscribeTooltip(ui->padConnectionBox, tooltips.settings.pad_connection);
|
SubscribeTooltip(ui->padConnectionBox, tooltips.settings.pad_connection);
|
||||||
|
|
||||||
|
m_emu_settings->EnhanceCheckBox(ui->wiimoteScanBox, emu_settings_type::WiimoteScan);
|
||||||
|
SubscribeTooltip(ui->wiimoteScanBox, tooltips.settings.wiimote_scan);
|
||||||
|
|
||||||
m_emu_settings->EnhanceCheckBox(ui->showMoveCursorBox, emu_settings_type::ShowMoveCursor);
|
m_emu_settings->EnhanceCheckBox(ui->showMoveCursorBox, emu_settings_type::ShowMoveCursor);
|
||||||
SubscribeTooltip(ui->showMoveCursorBox, tooltips.settings.show_move_cursor);
|
SubscribeTooltip(ui->showMoveCursorBox, tooltips.settings.show_move_cursor);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1785,6 +1785,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="wiimoteScanBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Wiimote Continuous Scanning</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="showMoveCursorBox">
|
<widget class="QCheckBox" name="showMoveCursorBox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|
|
||||||
|
|
@ -233,6 +233,7 @@ public:
|
||||||
|
|
||||||
const QString pad_mode = tr("Single-threaded: All pad handlers run on the same thread sequentially.\nMulti-threaded: Each pad handler has its own thread.\nOnly use multi-threaded if you can spare the extra threads.");
|
const QString pad_mode = tr("Single-threaded: All pad handlers run on the same thread sequentially.\nMulti-threaded: Each pad handler has its own thread.\nOnly use multi-threaded if you can spare the extra threads.");
|
||||||
const QString pad_connection = tr("Shows all configured pads as always connected ingame even if they are physically disconnected.");
|
const QString pad_connection = tr("Shows all configured pads as always connected ingame even if they are physically disconnected.");
|
||||||
|
const QString wiimote_scan = tr("Enables continuous scanning for Wiimotes. Required for some adapters like Mayflash DolphinBar to work correctly when hot-plugging.");
|
||||||
const QString keyboard_handler = tr("Some games support native keyboard input.\nBasic will work in these cases.");
|
const QString keyboard_handler = tr("Some games support native keyboard input.\nBasic will work in these cases.");
|
||||||
const QString mouse_handler = tr("Some games support native mouse input.\nBasic or Raw will work in these cases.");
|
const QString mouse_handler = tr("Some games support native mouse input.\nBasic or Raw will work in these cases.");
|
||||||
const QString music_handler = tr("Currently only used for cellMusic emulation.\nSelect Qt to use the default output device of your operating system.\nThis may not be able to play all audio formats.");
|
const QString music_handler = tr("Currently only used for cellMusic emulation.\nSelect Qt to use the default output device of your operating system.\nThis may not be able to play all audio formats.");
|
||||||
|
|
|
||||||
125
rpcs3/rpcs3qt/wiimote_settings_dialog.cpp
Normal file
125
rpcs3/rpcs3qt/wiimote_settings_dialog.cpp
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "wiimote_settings_dialog.h"
|
||||||
|
#include "Emu/System.h"
|
||||||
|
#include "Emu/Io/WiimoteManager.h"
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPixmap>
|
||||||
|
|
||||||
|
wiimote_settings_dialog::wiimote_settings_dialog(QWidget* parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
, ui(new Ui::wiimote_settings_dialog)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
update_list();
|
||||||
|
connect(ui->scanButton, &QPushButton::clicked, this, [this] { update_list(); });
|
||||||
|
|
||||||
|
QTimer* timer = new QTimer(this);
|
||||||
|
connect(timer, &QTimer::timeout, this, &wiimote_settings_dialog::update_state);
|
||||||
|
timer->start(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
wiimote_settings_dialog::~wiimote_settings_dialog() = default;
|
||||||
|
|
||||||
|
void wiimote_settings_dialog::update_state()
|
||||||
|
{
|
||||||
|
int index = ui->wiimoteList->currentRow();
|
||||||
|
auto* wm = WiimoteManager::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;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto states = wm->get_states();
|
||||||
|
if (static_cast<size_t>(index) >= states.size())
|
||||||
|
{
|
||||||
|
ui->connectionStatus->setText(tr("N/A"));
|
||||||
|
ui->buttonState->setText(tr("N/A"));
|
||||||
|
ui->irData->setText(tr("N/A"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& state = states[index];
|
||||||
|
ui->connectionStatus->setText(state.connected ? tr("Connected") : tr("Disconnected"));
|
||||||
|
|
||||||
|
QStringList pressedButtons;
|
||||||
|
if (state.buttons & 0x0001) pressedButtons << tr("Left");
|
||||||
|
if (state.buttons & 0x0002) pressedButtons << tr("Right");
|
||||||
|
if (state.buttons & 0x0004) pressedButtons << tr("Down");
|
||||||
|
if (state.buttons & 0x0008) pressedButtons << tr("Up");
|
||||||
|
if (state.buttons & 0x0010) pressedButtons << tr("Plus");
|
||||||
|
if (state.buttons & 0x0100) pressedButtons << tr("2");
|
||||||
|
if (state.buttons & 0x0200) pressedButtons << tr("1");
|
||||||
|
if (state.buttons & 0x0400) pressedButtons << tr("B");
|
||||||
|
if (state.buttons & 0x0800) pressedButtons << tr("A");
|
||||||
|
if (state.buttons & 0x1000) pressedButtons << tr("Minus");
|
||||||
|
if (state.buttons & 0x8000) pressedButtons << tr("Home");
|
||||||
|
|
||||||
|
QString buttonText = QString("0x%1").arg(state.buttons, 4, 16, QChar('0')).toUpper();
|
||||||
|
if (!pressedButtons.isEmpty())
|
||||||
|
{
|
||||||
|
buttonText += " (" + pressedButtons.join(", ") + ")";
|
||||||
|
}
|
||||||
|
ui->buttonState->setText(buttonText);
|
||||||
|
|
||||||
|
QString irText;
|
||||||
|
QPixmap pixmap(ui->irVisual->size());
|
||||||
|
pixmap.fill(Qt::black);
|
||||||
|
QPainter painter(&pixmap);
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
|
// Draw center crosshair
|
||||||
|
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);
|
||||||
|
|
||||||
|
static const QColor colors[] = { Qt::red, Qt::green, Qt::blue, Qt::yellow };
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
if (state.ir[i].size > 0 && state.ir[i].x < 1023 && state.ir[i].y < 1023)
|
||||||
|
{
|
||||||
|
irText += 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
|
||||||
|
float x = ((1023 - state.ir[i].x) / 1023.0f) * pixmap.width();
|
||||||
|
float y = (state.ir[i].y / 767.0f) * pixmap.height();
|
||||||
|
|
||||||
|
painter.setPen(colors[i]);
|
||||||
|
painter.setBrush(colors[i]);
|
||||||
|
painter.drawEllipse(QPointF(x, y), 4, 4);
|
||||||
|
painter.drawText(QPointF(x + 6, y + 6), QString::number(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui->irVisual->setPixmap(pixmap);
|
||||||
|
ui->irData->setText(irText.isEmpty() ? tr("No IR data") : irText);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wiimote_settings_dialog::update_list()
|
||||||
|
{
|
||||||
|
ui->wiimoteList->clear();
|
||||||
|
auto* wm = WiimoteManager::get_instance();
|
||||||
|
if (!wm)
|
||||||
|
{
|
||||||
|
ui->wiimoteList->addItem(tr("Wiimote Manager not initialized."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t count = wm->get_device_count();
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
ui->wiimoteList->addItem(tr("No Wiimotes found."));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
ui->wiimoteList->addItem(tr("Wiimote #%1").arg(i + 1));
|
||||||
|
}
|
||||||
|
ui->wiimoteList->setCurrentRow(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
rpcs3/rpcs3qt/wiimote_settings_dialog.h
Normal file
17
rpcs3/rpcs3qt/wiimote_settings_dialog.h
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include "ui_wiimote_settings_dialog.h"
|
||||||
|
|
||||||
|
class wiimote_settings_dialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit wiimote_settings_dialog(QWidget* parent = nullptr);
|
||||||
|
~wiimote_settings_dialog();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Ui::wiimote_settings_dialog> ui;
|
||||||
|
void update_list();
|
||||||
|
void update_state();
|
||||||
|
};
|
||||||
179
rpcs3/rpcs3qt/wiimote_settings_dialog.ui
Normal file
179
rpcs3/rpcs3qt/wiimote_settings_dialog.ui
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>wiimote_settings_dialog</class>
|
||||||
|
<widget class="QDialog" name="wiimote_settings_dialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>600</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Wiimote Settings</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Connected Wiimotes</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="wiimoteList"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="detailsGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Wiimote Details</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="labelStatus">
|
||||||
|
<property name="text">
|
||||||
|
<string>Status:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLabel" name="connectionStatus">
|
||||||
|
<property name="text">
|
||||||
|
<string>Disconnected</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="labelButtons">
|
||||||
|
<property name="text">
|
||||||
|
<string>Buttons:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLabel" name="buttonState">
|
||||||
|
<property name="text">
|
||||||
|
<string>0x0000</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="labelIR">
|
||||||
|
<property name="text">
|
||||||
|
<string>IR Data:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLabel" name="irData">
|
||||||
|
<property name="text">
|
||||||
|
<string>None</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="irGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>IR Sensor View (All Pointers)</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="irVisual">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>256</width>
|
||||||
|
<height>192</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">background-color: black; border: 1px solid gray;</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="scanButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Refresh Scan</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>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>wiimote_settings_dialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>wiimote_settings_dialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
Loading…
Reference in a new issue