GunCon: Add relative pointer binding

This commit is contained in:
Stenzek
2023-09-20 16:56:12 +10:00
parent e63b2eec38
commit 7e07d2feb8
21 changed files with 779 additions and 507 deletions

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "controller.h"
@ -26,18 +26,24 @@ static const Controller::ControllerInfo* s_controller_info[] = {
&GunCon::INFO, &PlayStationMouse::INFO,
};
Controller::Controller(u32 index) : m_index(index) {}
Controller::Controller(u32 index) : m_index(index)
{
}
Controller::~Controller() = default;
void Controller::Reset() {}
void Controller::Reset()
{
}
bool Controller::DoState(StateWrapper& sw, bool apply_input_state)
{
return !sw.HasError();
}
void Controller::ResetTransferState() {}
void Controller::ResetTransferState()
{
}
bool Controller::Transfer(const u8 data_in, u8* data_out)
{
@ -50,7 +56,9 @@ float Controller::GetBindState(u32 index) const
return 0.0f;
}
void Controller::SetBindState(u32 index, float value) {}
void Controller::SetBindState(u32 index, float value)
{
}
u32 Controller::GetButtonStateBits() const
{
@ -67,11 +75,8 @@ std::optional<u32> Controller::GetAnalogInputBytes() const
return std::nullopt;
}
void Controller::LoadSettings(SettingsInterface& si, const char* section) {}
bool Controller::GetSoftwareCursor(std::string* image_path, float* image_scale, bool* relative_mode)
void Controller::LoadSettings(SettingsInterface& si, const char* section)
{
return false;
}
std::unique_ptr<Controller> Controller::Create(ControllerType type, u32 index)

View File

@ -87,9 +87,6 @@ public:
/// Loads/refreshes any per-controller settings.
virtual void LoadSettings(SettingsInterface& si, const char* section);
/// Returns the software cursor to use for this controller, if any.
virtual bool GetSoftwareCursor(std::string* image_path, float* image_scale, bool* relative_mode);
/// Creates a new controller of the specified type.
static std::unique_ptr<Controller> Create(ControllerType type, u32 index);

View File

@ -1010,21 +1010,30 @@ void GPU::UpdateCommandTickEvent()
m_command_tick_event->SetIntervalAndSchedule(GPUTicksToSystemTicks(m_pending_command_ticks));
}
bool GPU::ConvertScreenCoordinatesToBeamTicksAndLines(s32 window_x, s32 window_y, float x_scale, u32* out_tick,
u32* out_line) const
void GPU::ConvertScreenCoordinatesToDisplayCoordinates(float window_x, float window_y, float* display_x,
float* display_y) const
{
float left_padding, top_padding, scale;
CalculateDrawRect(g_gpu_device->GetWindowWidth(), g_gpu_device->GetWindowHeight(), &left_padding, &top_padding,
&scale, nullptr);
float scale;
const Common::Rectangle<float> draw_rc = CalculateDrawRect(
g_gpu_device->GetWindowWidth(), g_gpu_device->GetWindowHeight(), nullptr, nullptr, &scale, nullptr);
// convert coordinates to active display region, then to full display region
const float scaled_display_x = static_cast<float>(window_x) - left_padding;
const float scaled_display_y = static_cast<float>(window_y) - top_padding;
const float scaled_display_x = (window_x - draw_rc.left) / static_cast<float>(draw_rc.GetWidth());
const float scaled_display_y = (window_y - draw_rc.top) / static_cast<float>(draw_rc.GetHeight());
// scale back to internal resolution
float display_x = scaled_display_x / scale / x_scale;
float display_y = scaled_display_y / scale;
*display_x = scaled_display_x * static_cast<float>(m_crtc_state.display_width);
*display_y = scaled_display_y * static_cast<float>(m_crtc_state.display_height);
Log_DebugPrintf("win %.0f,%.0f -> disp %.2f,%.2f (size %u,%u frac %f,%f)", window_x, window_y, *display_x, *display_y,
m_crtc_state.display_width, m_crtc_state.display_height,
*display_x / static_cast<float>(m_crtc_state.display_width),
*display_y / static_cast<float>(m_crtc_state.display_height));
}
bool GPU::ConvertDisplayCoordinatesToBeamTicksAndLines(float display_x, float display_y, float x_scale, u32* out_tick,
u32* out_line) const
{
if (x_scale != 1.0f)
{
const float dw = static_cast<float>(m_crtc_state.display_width);
@ -1033,11 +1042,6 @@ bool GPU::ConvertScreenCoordinatesToBeamTicksAndLines(s32 window_x, s32 window_y
display_x = (((scaled_x + 1.0f) * 0.5f) * dw); // -1..1 -> 0..1
}
Log_DebugPrintf("win %d,%d -> disp %.2f,%.2f (size %u,%u frac %f,%f)", window_x, window_y, display_x, display_y,
m_crtc_state.display_width, m_crtc_state.display_height,
display_x / static_cast<float>(m_crtc_state.display_width),
display_y / static_cast<float>(m_crtc_state.display_height));
if (display_x < 0 || static_cast<u32>(display_x) >= m_crtc_state.display_width || display_y < 0 ||
static_cast<u32>(display_y) >= m_crtc_state.display_height)
{

View File

@ -171,8 +171,10 @@ public:
static std::unique_ptr<GPU> CreateSoftwareRenderer();
// Converts window coordinates into horizontal ticks and scanlines. Returns false if out of range. Used for lightguns.
bool ConvertScreenCoordinatesToBeamTicksAndLines(s32 window_x, s32 window_y, float x_scale, u32* out_tick,
u32* out_line) const;
void ConvertScreenCoordinatesToDisplayCoordinates(float window_x, float window_y, float* display_x,
float* display_y) const;
bool ConvertDisplayCoordinatesToBeamTicksAndLines(float display_x, float display_y, float x_scale, u32* out_tick,
u32* out_line) const;
// Returns the video clock frequency.
TickCount GetCRTCFrequency() const;

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "guncon.h"
@ -6,11 +6,13 @@
#include "host.h"
#include "system.h"
#include "util/imgui_manager.h"
#include "util/input_manager.h"
#include "util/state_wrapper.h"
#include "common/assert.h"
#include "common/path.h"
#include "common/string_util.h"
#include <array>
@ -19,13 +21,21 @@
Log_SetChannel(GunCon);
#endif
static constexpr std::array<u8, static_cast<size_t>(GunCon::Button::Count)> s_button_indices = {{13, 3, 14}};
static constexpr std::array<u8, static_cast<size_t>(GunCon::Binding::ButtonCount)> s_button_indices = {{13, 3, 14}};
GunCon::GunCon(u32 index) : Controller(index)
{
}
GunCon::~GunCon() = default;
GunCon::~GunCon()
{
if (!m_cursor_path.empty())
{
const u32 cursor_index = GetSoftwarePointerIndex();
if (cursor_index < InputManager::MAX_SOFTWARE_CURSORS)
ImGuiManager::ClearSoftwareCursor(cursor_index);
}
}
ControllerType GunCon::GetType() const
{
@ -71,12 +81,25 @@ float GunCon::GetBindState(u32 index) const
void GunCon::SetBindState(u32 index, float value)
{
const bool pressed = (value >= 0.5f);
if (index == static_cast<u32>(Button::ShootOffscreen))
if (index == static_cast<u32>(Binding::ShootOffscreen))
{
if (m_shoot_offscreen != pressed)
{
m_shoot_offscreen = pressed;
SetBindState(static_cast<u32>(Button::Trigger), pressed);
SetBindState(static_cast<u32>(Binding::Trigger), pressed);
}
return;
}
else if (index >= static_cast<u32>(Binding::ButtonCount))
{
if (index >= static_cast<u32>(Binding::BindingCount) || !m_has_relative_binds)
return;
if (m_relative_pos[index - static_cast<u32>(Binding::RelativeLeft)] != value)
{
m_relative_pos[index - static_cast<u32>(Binding::RelativeLeft)] = value;
UpdateSoftwarePointerPosition();
}
return;
@ -181,18 +204,18 @@ bool GunCon::Transfer(const u8 data_in, u8* data_out)
void GunCon::UpdatePosition()
{
// get screen coordinates
const auto& [fmouse_x, fmouse_y] = InputManager::GetPointerAbsolutePosition(0);
const s32 mouse_x = static_cast<s32>(fmouse_x);
const s32 mouse_y = static_cast<s32>(fmouse_y);
float display_x, display_y;
const auto& [window_x, window_y] =
(m_has_relative_binds) ? GetAbsolutePositionFromRelativeAxes() : InputManager::GetPointerAbsolutePosition(0);
g_gpu->ConvertScreenCoordinatesToDisplayCoordinates(window_x, window_y, &display_x, &display_y);
// are we within the active display area?
u32 tick, line;
if (mouse_x < 0 || mouse_y < 0 ||
!g_gpu->ConvertScreenCoordinatesToBeamTicksAndLines(mouse_x, mouse_y, m_x_scale, &tick, &line) ||
if (display_x < 0 || display_y < 0 ||
!g_gpu->ConvertDisplayCoordinatesToBeamTicksAndLines(display_x, display_y, m_x_scale, &tick, &line) ||
m_shoot_offscreen)
{
Log_DebugPrintf("Lightgun out of range for window coordinates %d,%d", mouse_x, mouse_y);
Log_DebugPrintf("Lightgun out of range for window coordinates %.0f,%.0f", window_x, window_y);
m_position_x = 0x01;
m_position_y = 0x0A;
return;
@ -202,8 +225,34 @@ void GunCon::UpdatePosition()
const double divider = static_cast<double>(g_gpu->GetCRTCFrequency()) / 8000000.0;
m_position_x = static_cast<u16>(static_cast<float>(tick) / static_cast<float>(divider));
m_position_y = static_cast<u16>(line);
Log_DebugPrintf("Lightgun window coordinates %d,%d -> tick %u line %u 8mhz ticks %u", mouse_x, mouse_y, tick, line,
m_position_x);
Log_DebugPrintf("Lightgun window coordinates %.0f,%.0f -> tick %u line %u 8mhz ticks %u", display_x, display_y, tick,
line, m_position_x);
}
std::pair<float, float> GunCon::GetAbsolutePositionFromRelativeAxes() const
{
const float screen_rel_x = (((m_relative_pos[1] > 0.0f) ? m_relative_pos[1] : -m_relative_pos[0]) + 1.0f) * 0.5f;
const float screen_rel_y = (((m_relative_pos[3] > 0.0f) ? m_relative_pos[3] : -m_relative_pos[2]) + 1.0f) * 0.5f;
return std::make_pair(screen_rel_x * ImGuiManager::GetWindowWidth(), screen_rel_y * ImGuiManager::GetWindowHeight());
}
bool GunCon::CanUseSoftwareCursor() const
{
return (InputManager::MAX_POINTER_DEVICES + m_index) < InputManager::MAX_SOFTWARE_CURSORS;
}
u32 GunCon::GetSoftwarePointerIndex() const
{
return m_has_relative_binds ? (InputManager::MAX_POINTER_DEVICES + m_index) : 0;
}
void GunCon::UpdateSoftwarePointerPosition()
{
if (m_cursor_path.empty() || !CanUseSoftwareCursor())
return;
const auto& [window_x, window_y] = GetAbsolutePositionFromRelativeAxes();
ImGuiManager::SetSoftwareCursorPosition(GetSoftwarePointerIndex(), window_x, window_y);
}
std::unique_ptr<GunCon> GunCon::Create(u32 index)
@ -212,16 +261,25 @@ std::unique_ptr<GunCon> GunCon::Create(u32 index)
}
static const Controller::ControllerBindingInfo s_binding_info[] = {
#define BUTTON(name, display_name, button, genb) \
#define BUTTON(name, display_name, binding, genb) \
{ \
name, display_name, static_cast<u32>(button), InputBindingInfo::Type::Button, genb \
name, display_name, static_cast<u32>(binding), InputBindingInfo::Type::Button, genb \
}
#define HALFAXIS(name, display_name, binding, genb) \
{ \
name, display_name, static_cast<u32>(binding), InputBindingInfo::Type::HalfAxis, genb \
}
// clang-format off
BUTTON("Trigger", TRANSLATE_NOOP("GunCon", "Trigger"), GunCon::Button::Trigger, GenericInputBinding::R2),
BUTTON("ShootOffscreen", TRANSLATE_NOOP("GunCon", "Shoot Offscreen"), GunCon::Button::ShootOffscreen, GenericInputBinding::L2),
BUTTON("A", TRANSLATE_NOOP("GunCon", "A"), GunCon::Button::A, GenericInputBinding::Cross),
BUTTON("B", TRANSLATE_NOOP("GunCon", "B"), GunCon::Button::B, GenericInputBinding::Circle),
BUTTON("Trigger", TRANSLATE_NOOP("GunCon", "Trigger"), GunCon::Binding::Trigger, GenericInputBinding::R2),
BUTTON("ShootOffscreen", TRANSLATE_NOOP("GunCon", "Shoot Offscreen"), GunCon::Binding::ShootOffscreen, GenericInputBinding::L2),
BUTTON("A", TRANSLATE_NOOP("GunCon", "A"), GunCon::Binding::A, GenericInputBinding::Cross),
BUTTON("B", TRANSLATE_NOOP("GunCon", "B"), GunCon::Binding::B, GenericInputBinding::Circle),
HALFAXIS("RelativeLeft", TRANSLATE_NOOP("GunCon", "Relative Left"), GunCon::Binding::RelativeLeft, GenericInputBinding::Unknown),
HALFAXIS("RelativeRight", TRANSLATE_NOOP("GunCon", "Relative Right"), GunCon::Binding::RelativeRight, GenericInputBinding::Unknown),
HALFAXIS("RelativeUp", TRANSLATE_NOOP("GunCon", "Relative Up"), GunCon::Binding::RelativeUp, GenericInputBinding::Unknown),
HALFAXIS("RelativeDown", TRANSLATE_NOOP("GunCon", "Relative Down"), GunCon::Binding::RelativeDown, GenericInputBinding::Unknown),
// clang-format on
#undef BUTTON
@ -234,6 +292,10 @@ static const SettingInfo s_settings[] = {
{SettingInfo::Type::Float, "CrosshairScale", TRANSLATE_NOOP("GunCon", "Crosshair Image Scale"),
TRANSLATE_NOOP("GunCon", "Scale of crosshair image on screen."), "1.0", "0.0001", "100.0", "0.10", "%.0f%%", nullptr,
100.0f},
{SettingInfo::Type::String, "CrosshairColor", TRANSLATE_NOOP("GunCon", "Cursor Color"),
TRANSLATE_NOOP("USB", "Applies a color to the chosen crosshair images, can be used for multiple players. Specify in "
"HTML/CSS format (e.g. #aabbcc)"),
"#ffffff"},
{SettingInfo::Type::Float, "XScale", TRANSLATE_NOOP("GunCon", "X Scale"),
TRANSLATE_NOOP("GunCon", "Scales X coordinates relative to the center of the screen."), "1.0", "0.01", "2.0", "0.01",
"%.0f%%", nullptr, 100.0f}};
@ -251,24 +313,59 @@ void GunCon::LoadSettings(SettingsInterface& si, const char* section)
{
Controller::LoadSettings(si, section);
m_crosshair_image_path = si.GetStringValue(section, "CrosshairImagePath");
m_x_scale = si.GetFloatValue(section, "XScale", 1.0f);
std::string cursor_path = si.GetStringValue(section, "CrosshairImagePath");
const float cursor_scale = si.GetFloatValue(section, "CrosshairScale", 1.0f);
u32 cursor_color = 0xFFFFFF;
if (std::string cursor_color_str = si.GetStringValue(section, "CrosshairColor", ""); !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();
}
#ifndef __ANDROID__
if (m_crosshair_image_path.empty())
m_crosshair_image_path = Path::Combine(EmuFolders::Resources, "images/crosshair.png");
if (cursor_path.empty())
cursor_path = Path::Combine(EmuFolders::Resources, "images/crosshair.png");
#endif
m_crosshair_image_scale = si.GetFloatValue(section, "CrosshairScale", 1.0f);
const s32 prev_pointer_index = GetSoftwarePointerIndex();
m_x_scale = si.GetFloatValue(section, "XScale", 1.0f);
}
bool GunCon::GetSoftwareCursor(std::string* image_path, float* image_scale, bool* relative_mode)
{
if (m_crosshair_image_path.empty())
return false;
*image_path = m_crosshair_image_path;
*image_scale = m_crosshair_image_scale;
*relative_mode = false;
return true;
m_has_relative_binds = (si.ContainsValue(section, "RelativeLeft") || si.ContainsValue(section, "RelativeRight") ||
si.ContainsValue(section, "RelativeUp") || si.ContainsValue(section, "RelativeDown"));
const s32 new_pointer_index = GetSoftwarePointerIndex();
if (prev_pointer_index != new_pointer_index || m_cursor_path != cursor_path || m_cursor_scale != cursor_scale ||
m_cursor_color != cursor_color)
{
if (prev_pointer_index != new_pointer_index &&
static_cast<u32>(prev_pointer_index) < InputManager::MAX_SOFTWARE_CURSORS)
{
ImGuiManager::ClearSoftwareCursor(prev_pointer_index);
}
// Pointer changed, so need to update software cursor.
const bool had_software_cursor = m_cursor_path.empty();
m_cursor_path = std::move(cursor_path);
m_cursor_scale = cursor_scale;
m_cursor_color = cursor_color;
if (static_cast<u32>(new_pointer_index) < InputManager::MAX_SOFTWARE_CURSORS)
{
if (!m_cursor_path.empty())
{
ImGuiManager::SetSoftwareCursor(new_pointer_index, m_cursor_path, m_cursor_scale, m_cursor_color);
if (m_has_relative_binds)
UpdateSoftwarePointerPosition();
}
else if (had_software_cursor)
{
ImGuiManager::ClearSoftwareCursor(new_pointer_index);
}
}
}
}

View File

@ -10,13 +10,19 @@
class GunCon final : public Controller
{
public:
enum class Button : u8
enum class Binding : u8
{
Trigger = 0,
A = 1,
B = 2,
ShootOffscreen = 3,
Count
ButtonCount = 4,
RelativeLeft = 4,
RelativeRight = 5,
RelativeUp = 6,
RelativeDown = 7,
BindingCount = 8,
};
static const Controller::ControllerInfo INFO;
@ -30,9 +36,8 @@ public:
void Reset() override;
bool DoState(StateWrapper& sw, bool apply_input_state) override;
void LoadSettings(SettingsInterface& si, const char* section) override;
bool GetSoftwareCursor(std::string* image_path, float* image_scale, bool* relative_mode) override;
float GetBindState(u32 index) const override;
void SetBindState(u32 index, float value) override;
@ -41,8 +46,6 @@ public:
bool Transfer(const u8 data_in, u8* data_out) override;
private:
void UpdatePosition();
enum class TransferState : u8
{
Idle,
@ -56,15 +59,27 @@ private:
YMSB
};
std::string m_crosshair_image_path;
float m_crosshair_image_scale = 1.0f;
void UpdatePosition();
// 0..1, not -1..1.
std::pair<float, float> GetAbsolutePositionFromRelativeAxes() const;
bool CanUseSoftwareCursor() const;
u32 GetSoftwarePointerIndex() const;
void UpdateSoftwarePointerPosition();
std::string m_cursor_path;
float m_cursor_scale = 1.0f;
u32 m_cursor_color = 0xFFFFFFFFu;
float m_x_scale = 1.0f;
float m_relative_pos[4] = {};
// buttons are active low
u16 m_button_state = UINT16_C(0xFFFF);
u16 m_position_x = 0;
u16 m_position_y = 0;
bool m_shoot_offscreen = false;
bool m_has_relative_binds = false;
TransferState m_transfer_state = TransferState::Idle;
};

View File

@ -79,9 +79,6 @@ void ReportFormattedDebuggerMessage(const char* format, ...);
/// such as compiling shaders when starting up.
void DisplayLoadingScreen(const char* message, int progress_min = -1, int progress_max = -1, int progress_value = -1);
/// Enables "relative" mouse mode, locking the cursor position and returning relative coordinates.
void SetMouseMode(bool relative, bool hide_cursor);
/// Safely executes a function on the VM thread.
void RunOnCPUThread(std::function<void()> function, bool block = false);

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "playstation_mouse.h"
@ -6,7 +6,6 @@
#include "host.h"
#include "system.h"
#include "util/input_manager.h"
#include "util/state_wrapper.h"
#include "common/assert.h"
@ -16,13 +15,11 @@
Log_SetChannel(PlayStationMouse);
static constexpr std::array<u8, static_cast<size_t>(PlayStationMouse::Button::Count)> s_button_indices = {{11, 10}};
static constexpr std::array<u8, static_cast<size_t>(PlayStationMouse::Binding::ButtonCount)> s_button_indices = {
{11, 10}};
PlayStationMouse::PlayStationMouse(u32 index) : Controller(index)
{
const auto& [x, y] = InputManager::GetPointerAbsolutePosition(0);
m_last_host_position_x = static_cast<s32>(x);
m_last_host_position_y = static_cast<s32>(y);
}
PlayStationMouse::~PlayStationMouse() = default;
@ -43,11 +40,21 @@ bool PlayStationMouse::DoState(StateWrapper& sw, bool apply_input_state)
return false;
u16 button_state = m_button_state;
u8 delta_x = m_delta_x;
u8 delta_y = m_delta_y;
float delta_x = m_delta_x;
float delta_y = m_delta_y;
sw.Do(&button_state);
sw.Do(&delta_x);
sw.Do(&delta_y);
if (sw.GetVersion() >= 60)
{
sw.Do(&delta_x);
sw.Do(&delta_y);
}
else
{
u8 dummy = 0;
sw.Do(&dummy);
sw.Do(&dummy);
}
if (apply_input_state)
{
m_button_state = button_state;
@ -70,8 +77,15 @@ float PlayStationMouse::GetBindState(u32 index) const
void PlayStationMouse::SetBindState(u32 index, float value)
{
if (index > s_button_indices.size())
if (index >= s_button_indices.size())
{
if (index == static_cast<u32>(Binding::PointerX))
m_delta_x += value;
else if (index == static_cast<u32>(Binding::PointerY))
m_delta_y += value;
return;
}
if (value >= 0.5f)
m_button_state &= ~(u16(1) << s_button_indices[index]);
@ -138,15 +152,22 @@ bool PlayStationMouse::Transfer(const u8 data_in, u8* data_out)
case TransferState::DeltaX:
{
UpdatePosition();
*data_out = static_cast<u8>(m_delta_x);
const float delta_x =
std::clamp(std::floor(m_delta_x * m_sensitivity_x), static_cast<float>(std::numeric_limits<s8>::min()),
static_cast<float>(std::numeric_limits<s8>::max()));
m_delta_x -= delta_x / m_sensitivity_x;
*data_out = static_cast<s8>(delta_x);
m_transfer_state = TransferState::DeltaY;
return true;
}
case TransferState::DeltaY:
{
*data_out = static_cast<u8>(m_delta_y);
const float delta_y =
std::clamp(std::floor(m_delta_y * m_sensitivity_y), static_cast<float>(std::numeric_limits<s8>::min()),
static_cast<float>(std::numeric_limits<s8>::max()));
m_delta_y -= delta_y / m_sensitivity_x;
*data_out = static_cast<s8>(delta_y);
m_transfer_state = TransferState::Idle;
return false;
}
@ -158,22 +179,12 @@ bool PlayStationMouse::Transfer(const u8 data_in, u8* data_out)
}
}
void PlayStationMouse::UpdatePosition()
void PlayStationMouse::LoadSettings(SettingsInterface& si, const char* section)
{
// get screen coordinates
const auto& [fmouse_x, fmouse_y] = InputManager::GetPointerAbsolutePosition(0);
const s32 mouse_x = static_cast<s32>(fmouse_x);
const s32 mouse_y = static_cast<s32>(fmouse_y);
const s32 delta_x = mouse_x - m_last_host_position_x;
const s32 delta_y = mouse_y - m_last_host_position_y;
m_last_host_position_x = mouse_x;
m_last_host_position_y = mouse_y;
Controller::LoadSettings(si, section);
if (delta_x != 0 || delta_y != 0)
Log_DevPrintf("dx=%d, dy=%d", delta_x, delta_y);
m_delta_x = static_cast<s8>(std::clamp<s32>(delta_x, std::numeric_limits<s8>::min(), std::numeric_limits<s8>::max()));
m_delta_y = static_cast<s8>(std::clamp<s32>(delta_y, std::numeric_limits<s8>::min(), std::numeric_limits<s8>::max()));
m_sensitivity_x = si.GetFloatValue(section, "SensitivityX", 1.0f);
m_sensitivity_y = si.GetFloatValue(section, "SensitivityY", 1.0f);
}
std::unique_ptr<PlayStationMouse> PlayStationMouse::Create(u32 index)
@ -188,17 +199,20 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
}
// clang-format off
BUTTON("Left", TRANSLATE_NOOP("PlayStationMouse", "Left Button"), PlayStationMouse::Button::Left, GenericInputBinding::Cross),
BUTTON("Right", TRANSLATE_NOOP("PlayStationMouse", "Right Button"), PlayStationMouse::Button::Right, GenericInputBinding::Circle),
{ "Pointer", TRANSLATE_NOOP("PlaystationMouse", "Pointer"), static_cast<u32>(PlayStationMouse::Binding::PointerX), InputBindingInfo::Type::Pointer, GenericInputBinding::Unknown },
BUTTON("Left", TRANSLATE_NOOP("PlayStationMouse", "Left Button"), PlayStationMouse::Binding::Left, GenericInputBinding::Cross),
BUTTON("Right", TRANSLATE_NOOP("PlayStationMouse", "Right Button"), PlayStationMouse::Binding::Right, GenericInputBinding::Circle),
// clang-format on
#undef BUTTON
};
static const SettingInfo s_settings[] = {
{SettingInfo::Type::Boolean, "RelativeMouseMode", TRANSLATE_NOOP("PlayStationMouse", "Relative Mouse Mode"),
TRANSLATE_NOOP("PlayStationMouse", "Locks the mouse cursor to the window, use for FPS games."), "false", nullptr,
nullptr, nullptr, nullptr, nullptr, 0.0f},
{SettingInfo::Type::Float, "SensitivityX", TRANSLATE_NOOP("PlayStationMouse", "Horizontal Sensitivity"),
TRANSLATE_NOOP("PlayStationMouse", "Adjusts the correspondance between physical and virtual mouse movement."), "1.0",
"0.01", "2.0", "0.01", "%.0f", nullptr, 100.0f},
{SettingInfo::Type::Float, "SensitivityY", TRANSLATE_NOOP("PlayStationMouse", "Vertical Sensitivity"),
TRANSLATE_NOOP("PlayStationMouse", "Adjusts the correspondance between physical and virtual mouse movement."), "1.0",
"0.01", "2.0", "0.01", "%.0f", nullptr, 100.0f},
};
const Controller::ControllerInfo PlayStationMouse::INFO = {ControllerType::PlayStationMouse,
@ -208,17 +222,4 @@ const Controller::ControllerInfo PlayStationMouse::INFO = {ControllerType::PlayS
countof(s_binding_info),
s_settings,
countof(s_settings),
Controller::VibrationCapabilities::NoVibration};
void PlayStationMouse::LoadSettings(SettingsInterface& si, const char* section)
{
Controller::LoadSettings(si, section);
m_use_relative_mode = si.GetBoolValue(section, "RelativeMouseMode", false);
}
bool PlayStationMouse::GetSoftwareCursor(std::string* image_path, float* image_scale, bool* relative_mode)
{
*relative_mode = m_use_relative_mode;
return m_use_relative_mode;
}
Controller::VibrationCapabilities::NoVibration};

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
@ -10,11 +10,15 @@
class PlayStationMouse final : public Controller
{
public:
enum class Button : u8
enum class Binding : u8
{
Left = 0,
Right = 1,
Count
ButtonCount = 2,
PointerX = 2,
PointerY = 3,
BindingCount = 4,
};
static const Controller::ControllerInfo INFO;
@ -36,11 +40,8 @@ public:
bool Transfer(const u8 data_in, u8* data_out) override;
void LoadSettings(SettingsInterface& si, const char* section) override;
bool GetSoftwareCursor(std::string* image_path, float* image_scale, bool* relative_mode) override;
private:
void UpdatePosition();
enum class TransferState : u8
{
Idle,
@ -52,15 +53,13 @@ private:
DeltaY
};
s32 m_last_host_position_x = 0;
s32 m_last_host_position_y = 0;
float m_sensitivity_x = 1.0f;
float m_sensitivity_y = 1.0f;
// buttons are active low
u16 m_button_state = UINT16_C(0xFFFF);
s8 m_delta_x = 0;
s8 m_delta_y = 0;
float m_delta_x = 0;
float m_delta_y = 0;
TransferState m_transfer_state = TransferState::Idle;
bool m_use_relative_mode = false;
};

View File

@ -5,7 +5,7 @@
#include "types.h"
static constexpr u32 SAVE_STATE_MAGIC = 0x43435544;
static constexpr u32 SAVE_STATE_VERSION = 59;
static constexpr u32 SAVE_STATE_VERSION = 60;
static constexpr u32 SAVE_STATE_MINIMUM_VERSION = 42;
static_assert(SAVE_STATE_VERSION >= SAVE_STATE_MINIMUM_VERSION);

View File

@ -1398,7 +1398,6 @@ bool System::BootSystem(SystemBootParameters parameters)
// Good to go.
s_state = State::Running;
UpdateSoftwareCursor();
SPU::GetOutputStream()->SetPaused(false);
FullscreenUI::OnSystemStarted();
@ -1632,7 +1631,6 @@ void System::DestroySystem()
if (s_keep_gpu_device_on_shutdown && g_gpu_device)
{
g_gpu_device->SetDisplayMaxFPS(0.0f);
UpdateSoftwareCursor();
}
else
{
@ -3673,18 +3671,14 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
{
UpdateControllers();
ResetControllers();
UpdateSoftwareCursor();
controllers_updated = true;
}
}
if (IsValid() && !controllers_updated)
{
UpdateControllerSettings();
UpdateSoftwareCursor();
}
}
if (IsValid() && !controllers_updated)
UpdateControllerSettings();
if (g_settings.multitap_mode != old_settings.multitap_mode)
UpdateMultitaps();
@ -4562,38 +4556,6 @@ void System::ToggleSoftwareRendering()
ResetPerformanceCounters();
}
void System::UpdateSoftwareCursor()
{
if (!IsValid())
{
Host::SetMouseMode(false, false);
ImGuiManager::ClearSoftwareCursor(0);
return;
}
std::string image_path;
float image_scale = 1.0f;
bool relative_mode = false;
bool hide_cursor = false;
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{
Controller* controller = System::GetController(i);
if (controller && controller->GetSoftwareCursor(&image_path, &image_scale, &relative_mode))
{
hide_cursor = true;
break;
}
}
Host::SetMouseMode(relative_mode, hide_cursor);
if (!image_path.empty())
ImGuiManager::SetSoftwareCursor(0, std::move(image_path), image_scale);
else
ImGuiManager::ClearSoftwareCursor(0);
}
void System::RequestDisplaySize(float scale /*= 0.0f*/)
{
if (!IsValid())
@ -4638,7 +4600,9 @@ bool System::PresentDisplay(bool allow_skip_present)
FullscreenUI::Render();
ImGuiManager::RenderTextOverlays();
ImGuiManager::RenderOSDMessages();
ImGuiManager::RenderSoftwareCursors();
if (s_state == State::Running)
ImGuiManager::RenderSoftwareCursors();
}
// Debug windows are always rendered, otherwise mouse input breaks on skip.

View File

@ -456,9 +456,6 @@ bool ShouldUseVSync();
/// Quick switch between software and hardware rendering.
void ToggleSoftwareRendering();
/// Updates software cursor state, based on controllers.
void UpdateSoftwareCursor();
/// Resizes the render window to the display size, with an optional scale.
/// If the scale is set to 0, the internal resolution will be used, otherwise it is treated as a multiplier to 1x.
void RequestDisplaySize(float scale = 0.0f);