USB-GunCon2: Support binding to controller

This commit is contained in:
Stenzek 2023-07-23 15:14:13 +10:00 committed by Connor McLaughlin
parent 2703b91e41
commit 228f4f1010
3 changed files with 145 additions and 12 deletions

View File

@ -90,6 +90,8 @@ static std::vector<u8> s_standard_font_data;
static std::vector<u8> s_fixed_font_data;
static std::vector<u8> s_icon_font_data;
static float s_window_width;
static float s_window_height;
static Common::Timer s_last_render_time;
// cached copies of WantCaptureKeyboard/Mouse, used to know when to dispatch events
@ -196,12 +198,24 @@ void ImGuiManager::Shutdown(bool clear_state)
UnloadFontData();
}
float ImGuiManager::GetWindowWidth()
{
return s_window_width;
}
float ImGuiManager::GetWindowHeight()
{
return s_window_height;
}
void ImGuiManager::WindowResized()
{
const u32 new_width = g_gs_device ? g_gs_device->GetWindowWidth() : 0;
const u32 new_height = g_gs_device ? g_gs_device->GetWindowHeight() : 0;
ImGui::GetIO().DisplaySize = ImVec2(static_cast<float>(new_width), static_cast<float>(new_height));
s_window_width = static_cast<float>(new_width);
s_window_height = static_cast<float>(new_height);
ImGui::GetIO().DisplaySize = ImVec2(s_window_width, s_window_height);
UpdateScale();

View File

@ -41,6 +41,10 @@ namespace ImGuiManager
/// Frees all ImGui resources.
void Shutdown(bool clear_state);
/// Returns the size of the display window. Can be safely called from any thread.
float GetWindowWidth();
float GetWindowHeight();
/// Updates internal state when the window is size.
void WindowResized();

View File

@ -17,6 +17,7 @@
#include "GS/GS.h"
#include "Host.h"
#include "ImGui/ImGuiManager.h"
#include "Input/InputManager.h"
#include "StateWrapper.h"
#include "USB/USB.h"
@ -26,6 +27,8 @@
#include "USB/usb-lightgun/guncon2.h"
#include "VMManager.h"
#include "common/StringUtil.h"
#include <tuple>
namespace usb_lightgun
@ -52,6 +55,10 @@ namespace usb_lightgun
BID_START = 15,
BID_SHOOT_OFFSCREEN = 16,
BID_RECALIBRATE = 17,
BID_RELATIVE_LEFT = 18,
BID_RELATIVE_RIGHT = 19,
BID_RELATIVE_UP = 20,
BID_RELATIVE_DOWN = 21,
};
// Right pain in the arse. Different games seem to have different scales..
@ -133,6 +140,7 @@ namespace usb_lightgun
//////////////////////////////////////////////////////////////////////////
// Configuration
//////////////////////////////////////////////////////////////////////////
bool has_relative_binds = false;
bool custom_config = false;
u32 screen_width = 640;
u32 screen_height = 240;
@ -145,6 +153,10 @@ namespace usb_lightgun
// Host State (Not Saved)
//////////////////////////////////////////////////////////////////////////
u32 button_state = 0;
std::string cursor_path;
float cursor_scale = 1.0f;
u32 cursor_color = 0xFFFFFFFF;
float relative_pos[4] = {};
//////////////////////////////////////////////////////////////////////////
// Device State (Saved)
@ -162,6 +174,11 @@ namespace usb_lightgun
void AutoConfigure();
std::tuple<s16, s16> CalculatePosition();
// 0..1, not -1..1.
std::pair<float, float> GetAbsolutePositionFromRelativeAxes() const;
u32 GetSoftwarePointerIndex() const;
void UpdateSoftwarePointerPosition();
};
static const USBDescStrings desc_strings = {
@ -323,6 +340,10 @@ namespace usb_lightgun
static void usb_hid_unrealize(USBDevice* dev)
{
GunCon2State* us = USB_CONTAINER_OF(dev, GunCon2State, dev);
if (!us->cursor_path.empty())
ImGuiManager::ClearSoftwareCursor(us->GetSoftwarePointerIndex());
delete us;
}
@ -359,8 +380,9 @@ namespace usb_lightgun
std::tuple<s16, s16> GunCon2State::CalculatePosition()
{
float pointer_x, pointer_y;
const std::pair<float, float> abs_pos(InputManager::GetPointerAbsolutePosition(0));
GSTranslateWindowToDisplayCoordinates(abs_pos.first, abs_pos.second, &pointer_x, &pointer_y);
const auto& [window_x, window_y] =
(has_relative_binds) ? GetAbsolutePositionFromRelativeAxes() : InputManager::GetPointerAbsolutePosition(0);
GSTranslateWindowToDisplayCoordinates(window_x, window_y, &pointer_x, &pointer_y);
s16 pos_x, pos_y;
if (pointer_x < 0.0f || pointer_y < 0.0f)
@ -403,6 +425,29 @@ namespace usb_lightgun
return std::tie(pos_x, pos_y);
}
std::pair<float, float> GunCon2State::GetAbsolutePositionFromRelativeAxes() const
{
const float screen_rel_x = (((relative_pos[1] > 0.0f) ? relative_pos[1] : -relative_pos[0]) + 1.0f) * 0.5f;
const float screen_rel_y = (((relative_pos[3] > 0.0f) ? relative_pos[3] : -relative_pos[2]) + 1.0f) * 0.5f;
return std::make_pair(
screen_rel_x * ImGuiManager::GetWindowWidth(), screen_rel_y * ImGuiManager::GetWindowHeight());
}
u32 GunCon2State::GetSoftwarePointerIndex() const
{
return has_relative_binds ? (InputManager::MAX_POINTER_DEVICES + port) : 0;
}
void GunCon2State::UpdateSoftwarePointerPosition()
{
pxAssert(has_relative_binds);
if (cursor_path.empty())
return;
const auto& [window_x, window_y] = GetAbsolutePositionFromRelativeAxes();
ImGuiManager::SetSoftwareCursorPosition(GetSoftwarePointerIndex(), window_x, window_y);
}
const char* GunCon2Device::Name() const
{
return TRANSLATE_NOOP("USB", "GunCon 2");
@ -435,6 +480,8 @@ namespace usb_lightgun
usb_desc_init(&s->dev);
usb_ep_init(&s->dev);
UpdateSettings(&s->dev, si);
return &s->dev;
fail:
usb_hid_unrealize(&s->dev);
@ -457,6 +504,47 @@ namespace usb_lightgun
s->scale_x = USB::GetConfigFloat(si, s->port, TypeName(), "scale_x", DEFAULT_SCALE_X) / 100.0f;
s->scale_y = USB::GetConfigFloat(si, s->port, TypeName(), "scale_y", DEFAULT_SCALE_Y) / 100.0f;
}
// Pointer settings.
const std::string pointer_binding = USB::GetConfigString(si, s->port, TypeName(), "Pointer", "");
std::string cursor_path(USB::GetConfigString(si, s->port, TypeName(), "cursor_path"));
const float cursor_scale = USB::GetConfigFloat(si, s->port, TypeName(), "cursor_scale", 1.0f);
u32 cursor_color = 0xFFFFFF;
if (std::string cursor_color_str(USB::GetConfigString(si, s->port, TypeName(), "cursor_color")); !cursor_color_str.empty())
{
// Strip the leading hash, if it's a CSS style colour.
const std::optional<u32> cursor_color_opt(
StringUtil::FromChars<u32>(cursor_color_str[0] == '#' ?
std::string_view(cursor_color_str).substr(1) : std::string_view(cursor_color_str), 16));
if (cursor_color_opt.has_value())
cursor_color = cursor_color_opt.value();
}
const s32 prev_pointer_index = s->GetSoftwarePointerIndex();
s->has_relative_binds = (USB::ConfigKeyExists(si, s->port, TypeName(), "RelativeLeft") ||
USB::ConfigKeyExists(si, s->port, TypeName(), "RelativeRight") ||
USB::ConfigKeyExists(si, s->port, TypeName(), "RelativeUp") ||
USB::ConfigKeyExists(si, s->port, TypeName(), "RelativeDown"));
const s32 new_pointer_index = s->GetSoftwarePointerIndex();
if (prev_pointer_index != new_pointer_index || s->cursor_path != cursor_path ||
s->cursor_scale != cursor_scale || s->cursor_color != cursor_color)
{
if (prev_pointer_index != new_pointer_index)
ImGuiManager::ClearSoftwareCursor(prev_pointer_index);
// Pointer changed, so need to update software cursor.
if (!cursor_path.empty())
ImGuiManager::SetSoftwareCursor(new_pointer_index, cursor_path, cursor_scale, cursor_color);
else if (!s->cursor_path.empty())
ImGuiManager::ClearSoftwareCursor(new_pointer_index);
s->cursor_path = std::move(cursor_path);
s->cursor_scale = cursor_scale;
s->cursor_color = cursor_color;
}
}
float GunCon2Device::GetBindingValue(const USBDevice* dev, u32 bind_index) const
@ -471,11 +559,23 @@ namespace usb_lightgun
{
GunCon2State* s = USB_CONTAINER_OF(dev, GunCon2State, dev);
const u32 bit = 1u << bind_index;
if (value >= 0.5f)
s->button_state |= bit;
else
s->button_state &= ~bit;
if (bind_index < BID_RELATIVE_LEFT)
{
const u32 bit = 1u << bind_index;
if (value >= 0.5f)
s->button_state |= bit;
else
s->button_state &= ~bit;
}
else if (bind_index <= BID_RELATIVE_DOWN)
{
const u32 rel_index = bind_index - BID_RELATIVE_LEFT;
if (s->relative_pos[rel_index] != value)
{
s->relative_pos[rel_index] = value;
s->UpdateSoftwarePointerPosition();
}
}
}
std::span<const InputBindingInfo> GunCon2Device::Bindings(u32 subtype) const
@ -497,6 +597,10 @@ namespace usb_lightgun
{"C", TRANSLATE_NOOP("USB", "C"), InputBindingInfo::Type::Button, BID_C, GenericInputBinding::Triangle},
{"Select", TRANSLATE_NOOP("USB", "Select"), InputBindingInfo::Type::Button, BID_SELECT, GenericInputBinding::Select},
{"Start", TRANSLATE_NOOP("USB", "Start"), InputBindingInfo::Type::Button, BID_START, GenericInputBinding::Start},
{"RelativeLeft", TRANSLATE_NOOP("USB", "Relative Left"), InputBindingInfo::Type::HalfAxis, BID_RELATIVE_LEFT, GenericInputBinding::Unknown},
{"RelativeRight", TRANSLATE_NOOP("USB", "Relative Right"), InputBindingInfo::Type::HalfAxis, BID_RELATIVE_RIGHT, GenericInputBinding::Unknown},
{"RelativeUp", TRANSLATE_NOOP("USB", "Relative Up"), InputBindingInfo::Type::HalfAxis, BID_RELATIVE_UP, GenericInputBinding::Unknown},
{"RelativeDown", TRANSLATE_NOOP("USB", "Relative Down"), InputBindingInfo::Type::HalfAxis, BID_RELATIVE_DOWN, GenericInputBinding::Unknown},
};
return bindings;
@ -505,16 +609,27 @@ namespace usb_lightgun
std::span<const SettingInfo> GunCon2Device::Settings(u32 subtype) const
{
static constexpr const SettingInfo info[] = {
{SettingInfo::Type::Path, "cursor_path", "Cursor Path",
TRANSLATE_NOOP("USB", "Sets the crosshair image that this lightgun will use. Setting a crosshair image "
"will disable the system cursor."),
""},
{SettingInfo::Type::Float, "cursor_scale", TRANSLATE_NOOP("USB", "Cursor Scale"),
TRANSLATE_NOOP("USB", "Scales the crosshair image set above."), "1", "0.01", "10", "0.01", "%.0f%%",
nullptr, nullptr, 100.0f},
{SettingInfo::Type::String, "cursor_color", TRANSLATE_NOOP("USB", "Cursor Color"),
TRANSLATE_NOOP("USB", "Applys a color to the chosen crosshair images, can be used for multiple "
"players. Specify in HTML/CSS format (e.g. #aabbcc)"),
"#ffffff"},
{SettingInfo::Type::Boolean, "custom_config", TRANSLATE_NOOP("USB", "Manual Screen Configuration"),
TRANSLATE_NOOP("USB",
"Forces the use of the screen parameters below, instead of automatic parameters if available."),
"false"},
{SettingInfo::Type::Float, "scale_x", TRANSLATE_NOOP("USB", "X Scale (Sensitivity)"),
"Scales the position to simulate CRT curvature.", "100", "0", "200", "0.1", "%.2f%%", nullptr, nullptr,
1.0f},
TRANSLATE_NOOP("USB", "Scales the position to simulate CRT curvature."), "100", "0", "200", "0.1",
"%.2f%%", nullptr, nullptr, 1.0f},
{SettingInfo::Type::Float, "scale_y", TRANSLATE_NOOP("USB", "Y Scale (Sensitivity)"),
"Scales the position to simulate CRT curvature.", "100", "0", "200", "0.1", "%.2f%%", nullptr, nullptr,
1.0f},
TRANSLATE_NOOP("USB", "Scales the position to simulate CRT curvature."), "100", "0", "200", "0.1",
"%.2f%%", nullptr, nullptr, 1.0f},
{SettingInfo::Type::Float, "center_x", TRANSLATE_NOOP("USB", "Center X"),
TRANSLATE_NOOP("USB", "Sets the horizontal center position of the simulated screen."), "320", "0",
"1024", "1", "%.0fpx", nullptr, nullptr, 1.0f},