mirror of https://github.com/PCSX2/pcsx2.git
ImGuiManager: Add software cursor support
Can be used to render a crosshair for GunCon.
This commit is contained in:
parent
49d3338d4a
commit
24171787f8
|
@ -257,7 +257,7 @@ void Host::OnInputDeviceDisconnected(const std::string_view& identifier)
|
|||
{
|
||||
}
|
||||
|
||||
void Host::SetRelativeMouseMode(bool enabled)
|
||||
void Host::SetMouseMode(bool relative_mode, bool hide_cursor)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -414,7 +414,7 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread)
|
|||
connect(thread, &EmuThread::onAcquireRenderWindowRequested, this, &MainWindow::acquireRenderWindow, Qt::BlockingQueuedConnection);
|
||||
connect(thread, &EmuThread::onReleaseRenderWindowRequested, this, &MainWindow::releaseRenderWindow, Qt::BlockingQueuedConnection);
|
||||
connect(thread, &EmuThread::onResizeRenderWindowRequested, this, &MainWindow::displayResizeRequested);
|
||||
connect(thread, &EmuThread::onRelativeMouseModeRequested, this, &MainWindow::relativeMouseModeRequested);
|
||||
connect(thread, &EmuThread::onMouseModeRequested, this, &MainWindow::mouseModeRequested);
|
||||
connect(thread, &EmuThread::onVMStarting, this, &MainWindow::onVMStarting);
|
||||
connect(thread, &EmuThread::onVMStarted, this, &MainWindow::onVMStarted);
|
||||
connect(thread, &EmuThread::onVMPaused, this, &MainWindow::onVMPaused);
|
||||
|
@ -887,7 +887,8 @@ bool MainWindow::isRenderingToMain() const
|
|||
|
||||
bool MainWindow::shouldHideMouseCursor() const
|
||||
{
|
||||
return (isRenderingFullscreen() && Host::GetBoolSettingValue("UI", "HideMouseCursor", false)) || m_relative_mouse_mode;
|
||||
return ((isRenderingFullscreen() && Host::GetBoolSettingValue("UI", "HideMouseCursor", false)) ||
|
||||
m_relative_mouse_mode || m_hide_mouse_cursor);
|
||||
}
|
||||
|
||||
bool MainWindow::shouldHideMainWindow() const
|
||||
|
@ -2003,12 +2004,13 @@ void MainWindow::displayResizeRequested(qint32 width, qint32 height)
|
|||
QtUtils::ResizePotentiallyFixedSizeWindow(this, width, height + extra_height);
|
||||
}
|
||||
|
||||
void MainWindow::relativeMouseModeRequested(bool enabled)
|
||||
void MainWindow::mouseModeRequested(bool relative_mode, bool hide_cursor)
|
||||
{
|
||||
if (m_relative_mouse_mode == enabled)
|
||||
if (m_relative_mouse_mode == relative_mode && m_hide_mouse_cursor == hide_cursor)
|
||||
return;
|
||||
|
||||
m_relative_mouse_mode = enabled;
|
||||
m_relative_mouse_mode = relative_mode;
|
||||
m_hide_mouse_cursor = hide_cursor;
|
||||
if (m_display_widget && !s_vm_paused)
|
||||
updateDisplayWidgetCursor();
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ private Q_SLOTS:
|
|||
|
||||
std::optional<WindowInfo> acquireRenderWindow(bool recreate_window, bool fullscreen, bool render_to_main, bool surfaceless);
|
||||
void displayResizeRequested(qint32 width, qint32 height);
|
||||
void relativeMouseModeRequested(bool enabled);
|
||||
void mouseModeRequested(bool relative_mode, bool hide_cursor);
|
||||
void releaseRenderWindow();
|
||||
void focusDisplayWidget();
|
||||
|
||||
|
@ -293,6 +293,7 @@ private:
|
|||
|
||||
bool m_display_created = false;
|
||||
bool m_relative_mouse_mode = false;
|
||||
bool m_hide_mouse_cursor = false;
|
||||
bool m_was_paused_on_surface_loss = false;
|
||||
bool m_was_disc_change_request = false;
|
||||
bool m_is_closing = false;
|
||||
|
|
|
@ -1468,9 +1468,9 @@ void Host::OnInputDeviceDisconnected(const std::string_view& identifier)
|
|||
emit g_emu_thread->onInputDeviceDisconnected(identifier.empty() ? QString() : QString::fromUtf8(identifier.data(), identifier.size()));
|
||||
}
|
||||
|
||||
void Host::SetRelativeMouseMode(bool enabled)
|
||||
void Host::SetMouseMode(bool relative_mode, bool hide_cursor)
|
||||
{
|
||||
emit g_emu_thread->onRelativeMouseModeRequested(enabled);
|
||||
emit g_emu_thread->onMouseModeRequested(relative_mode, hide_cursor);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -119,7 +119,7 @@ Q_SIGNALS:
|
|||
std::optional<WindowInfo> onAcquireRenderWindowRequested(bool recreate_window, bool fullscreen, bool render_to_main, bool surfaceless);
|
||||
void onResizeRenderWindowRequested(qint32 width, qint32 height);
|
||||
void onReleaseRenderWindowRequested();
|
||||
void onRelativeMouseModeRequested(bool enabled);
|
||||
void onMouseModeRequested(bool relative_mode, bool hide_cursor);
|
||||
|
||||
/// Called when the VM is starting initialization, but has not been completed yet.
|
||||
void onVMStarting();
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "fmt/core.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "common/Image.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
|
@ -47,6 +48,17 @@
|
|||
|
||||
namespace ImGuiManager
|
||||
{
|
||||
struct SoftwareCursor
|
||||
{
|
||||
std::string image_path;
|
||||
std::unique_ptr<GSTexture> texture;
|
||||
u32 color;
|
||||
float scale;
|
||||
float extent_x;
|
||||
float extent_y;
|
||||
std::pair<float, float> pos;
|
||||
};
|
||||
|
||||
static void SetStyle();
|
||||
static void SetKeyMap();
|
||||
static bool LoadFontData();
|
||||
|
@ -57,6 +69,11 @@ namespace ImGuiManager
|
|||
static bool AddIconFonts(float size);
|
||||
static void AcquirePendingOSDMessages();
|
||||
static void DrawOSDMessages();
|
||||
static void CreateSoftwareCursorTextures();
|
||||
static void UpdateSoftwareCursorTexture(u32 index);
|
||||
static void DestroySoftwareCursorTextures();
|
||||
static void DrawSoftwareCursor(const SoftwareCursor& sc, const std::pair<float, float>& pos);
|
||||
static void DrawSoftwareCursors();
|
||||
} // namespace ImGuiManager
|
||||
|
||||
static float s_global_scale = 1.0f;
|
||||
|
@ -86,6 +103,8 @@ static std::unordered_map<u32, ImGuiKey> s_imgui_key_map;
|
|||
// need to keep track of this, so we can reinitialize on renderer switch
|
||||
static bool s_fullscreen_ui_was_initialized = false;
|
||||
|
||||
static std::array<ImGuiManager::SoftwareCursor, InputManager::MAX_SOFTWARE_CURSORS> s_software_cursors = {};
|
||||
|
||||
void ImGuiManager::SetFontPath(std::string path)
|
||||
{
|
||||
s_font_path = std::move(path);
|
||||
|
@ -146,6 +165,7 @@ bool ImGuiManager::Initialize()
|
|||
if (add_fullscreen_fonts)
|
||||
InitializeFullscreenUI();
|
||||
|
||||
CreateSoftwareCursorTextures();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -157,6 +177,8 @@ bool ImGuiManager::InitializeFullscreenUI()
|
|||
|
||||
void ImGuiManager::Shutdown(bool clear_state)
|
||||
{
|
||||
DestroySoftwareCursorTextures();
|
||||
|
||||
FullscreenUI::Shutdown(clear_state);
|
||||
ImGuiFullscreen::SetFonts(nullptr, nullptr, nullptr);
|
||||
if (clear_state)
|
||||
|
@ -672,6 +694,7 @@ void ImGuiManager::RenderOSD()
|
|||
|
||||
AcquirePendingOSDMessages();
|
||||
DrawOSDMessages();
|
||||
DrawSoftwareCursors();
|
||||
}
|
||||
|
||||
float ImGuiManager::GetGlobalScale()
|
||||
|
@ -810,3 +833,112 @@ bool ImGuiManager::ProcessGenericInputEvent(GenericInputBinding key, float value
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGuiManager::CreateSoftwareCursorTextures()
|
||||
{
|
||||
for (u32 i = 0; i < InputManager::MAX_POINTER_DEVICES; i++)
|
||||
{
|
||||
if (!s_software_cursors[i].image_path.empty())
|
||||
UpdateSoftwareCursorTexture(i);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiManager::DestroySoftwareCursorTextures()
|
||||
{
|
||||
for (u32 i = 0; i < InputManager::MAX_POINTER_DEVICES; i++)
|
||||
{
|
||||
s_software_cursors[i].texture.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiManager::UpdateSoftwareCursorTexture(u32 index)
|
||||
{
|
||||
SoftwareCursor& sc = s_software_cursors[index];
|
||||
if (sc.image_path.empty())
|
||||
{
|
||||
sc.texture.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
Common::RGBA8Image image;
|
||||
if (!image.LoadFromFile(sc.image_path.c_str()))
|
||||
{
|
||||
Console.Error("Failed to load software cursor %u image '%s'", index, sc.image_path.c_str());
|
||||
return;
|
||||
}
|
||||
sc.texture = std::unique_ptr<GSTexture>(g_gs_device->CreateTexture(image.GetWidth(), image.GetHeight(), 1, GSTexture::Format::Color));
|
||||
if (!sc.texture)
|
||||
{
|
||||
Console.Error(
|
||||
"Failed to upload %ux%u software cursor %u image '%s'", image.GetWidth(), image.GetHeight(), index, sc.image_path.c_str());
|
||||
return;
|
||||
}
|
||||
sc.texture->Update(GSVector4i(0, 0, image.GetWidth(), image.GetHeight()), image.GetPixels(), image.GetByteStride(), 0);
|
||||
|
||||
sc.extent_x = std::ceil(static_cast<float>(image.GetWidth()) * sc.scale * s_global_scale) / 2.0f;
|
||||
sc.extent_y = std::ceil(static_cast<float>(image.GetHeight()) * sc.scale * s_global_scale) / 2.0f;
|
||||
}
|
||||
|
||||
void ImGuiManager::DrawSoftwareCursor(const SoftwareCursor& sc, const std::pair<float, float>& pos)
|
||||
{
|
||||
if (!sc.texture)
|
||||
return;
|
||||
|
||||
const ImVec2 min(pos.first - sc.extent_x, pos.second - sc.extent_y);
|
||||
const ImVec2 max(pos.first + sc.extent_x, pos.second + sc.extent_y);
|
||||
|
||||
ImDrawList* dl = ImGui::GetForegroundDrawList();
|
||||
|
||||
dl->AddImage(
|
||||
reinterpret_cast<ImTextureID>(sc.texture.get()->GetNativeHandle()), min, max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), sc.color);
|
||||
}
|
||||
|
||||
void ImGuiManager::DrawSoftwareCursors()
|
||||
{
|
||||
// This one's okay to race, worst that happens is we render the wrong number of cursors for a frame.
|
||||
const u32 pointer_count = InputManager::MAX_POINTER_DEVICES;
|
||||
for (u32 i = 0; i < pointer_count; i++)
|
||||
DrawSoftwareCursor(s_software_cursors[i], InputManager::GetPointerAbsolutePosition(i));
|
||||
|
||||
for (u32 i = InputManager::MAX_POINTER_DEVICES; i < InputManager::MAX_SOFTWARE_CURSORS; i++)
|
||||
DrawSoftwareCursor(s_software_cursors[i], s_software_cursors[i].pos);
|
||||
}
|
||||
|
||||
void ImGuiManager::SetSoftwareCursor(u32 index, std::string image_path, float image_scale, u32 multiply_color)
|
||||
{
|
||||
MTGS::RunOnGSThread([index, image_path = std::move(image_path), image_scale, multiply_color]() {
|
||||
pxAssert(index < std::size(s_software_cursors));
|
||||
SoftwareCursor& sc = s_software_cursors[index];
|
||||
sc.color = multiply_color | 0xFF000000;
|
||||
if (sc.image_path == image_path && sc.scale == image_scale)
|
||||
return;
|
||||
|
||||
const bool is_hiding_or_showing = (image_path.empty() != sc.image_path.empty());
|
||||
sc.image_path = std::move(image_path);
|
||||
sc.scale = image_scale;
|
||||
if (MTGS::IsOpen())
|
||||
UpdateSoftwareCursorTexture(index);
|
||||
|
||||
// Hide the system cursor when we activate a software cursor.
|
||||
if (is_hiding_or_showing && index == 0)
|
||||
Host::RunOnCPUThread(&InputManager::UpdateHostMouseMode);
|
||||
});
|
||||
}
|
||||
|
||||
bool ImGuiManager::HasSoftwareCursor(u32 index)
|
||||
{
|
||||
return (index < s_software_cursors.size() && !s_software_cursors[index].image_path.empty());
|
||||
}
|
||||
|
||||
void ImGuiManager::ClearSoftwareCursor(u32 index)
|
||||
{
|
||||
SetSoftwareCursor(index, std::string(), 0.0f, 0);
|
||||
}
|
||||
|
||||
void ImGuiManager::SetSoftwareCursorPosition(u32 index, float pos_x, float pos_y)
|
||||
{
|
||||
pxAssert(index >= InputManager::MAX_POINTER_DEVICES);
|
||||
SoftwareCursor& sc = s_software_cursors[index];
|
||||
sc.pos.first = pos_x;
|
||||
sc.pos.second = pos_y;
|
||||
}
|
||||
|
|
|
@ -102,6 +102,14 @@ namespace ImGuiManager
|
|||
|
||||
/// Called on the CPU thread when any input event fires. Allows imgui to take over controller navigation.
|
||||
bool ProcessGenericInputEvent(GenericInputBinding key, float value);
|
||||
|
||||
/// Sets an image and scale for a software cursor. Software cursors can be used for things like crosshairs.
|
||||
void SetSoftwareCursor(u32 index, std::string image_path, float image_scale, u32 multiply_color = 0xFFFFFF);
|
||||
bool HasSoftwareCursor(u32 index);
|
||||
void ClearSoftwareCursor(u32 index);
|
||||
|
||||
/// Sets the position of a software cursor, used when we have relative coordinates such as controllers.
|
||||
void SetSoftwareCursorPosition(u32 index, float pos_x, float pos_y);
|
||||
} // namespace ImGuiManager
|
||||
|
||||
namespace Host
|
||||
|
|
|
@ -1309,6 +1309,11 @@ void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& bind
|
|||
for (u32 port = 0; port < USB::NUM_PORTS; port++)
|
||||
AddUSBBindings(binding_si, port);
|
||||
|
||||
UpdateHostMouseMode();
|
||||
}
|
||||
|
||||
void InputManager::UpdateHostMouseMode()
|
||||
{
|
||||
// Check for relative mode bindings, and enable if there's anything using it.
|
||||
bool has_relative_mode_bindings = !s_pointer_move_callbacks.empty();
|
||||
if (!has_relative_mode_bindings)
|
||||
|
@ -1324,7 +1329,9 @@ void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& bind
|
|||
}
|
||||
}
|
||||
}
|
||||
Host::SetRelativeMouseMode(has_relative_mode_bindings);
|
||||
|
||||
const bool has_software_cursor = ImGuiManager::HasSoftwareCursor(0);
|
||||
Host::SetMouseMode(has_relative_mode_bindings, has_relative_mode_bindings || has_software_cursor);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
|
@ -170,6 +170,10 @@ namespace InputManager
|
|||
static constexpr u32 MAX_POINTER_DEVICES = 1;
|
||||
static constexpr u32 MAX_POINTER_BUTTONS = 3;
|
||||
|
||||
/// Maximum number of software cursors. We allocate an extra two for USB devices with
|
||||
/// positioning data from the controller instead of a mouse.
|
||||
static constexpr u32 MAX_SOFTWARE_CURSORS = MAX_POINTER_BUTTONS + 2;
|
||||
|
||||
/// Returns a pointer to the external input source class, if present.
|
||||
InputSource* GetInputSourceInterface(InputSourceType type);
|
||||
|
||||
|
@ -287,6 +291,9 @@ namespace InputManager
|
|||
/// Updates relative pointer position. Can call from the UI thread, use when host supports relative coordinate reporting.
|
||||
void UpdatePointerRelativeDelta(u32 index, InputPointerAxis axis, float d, bool raw_input = false);
|
||||
|
||||
/// Updates host mouse mode (relative/cursor hiding).
|
||||
void UpdateHostMouseMode();
|
||||
|
||||
/// Called when a new input device is connected.
|
||||
void OnInputDeviceConnected(const std::string_view& identifier, const std::string_view& device_name);
|
||||
|
||||
|
@ -305,6 +312,6 @@ namespace Host
|
|||
/// Called when an input device is disconnected.
|
||||
void OnInputDeviceDisconnected(const std::string_view& identifier);
|
||||
|
||||
/// Enables relative mouse mode in the host.
|
||||
void SetRelativeMouseMode(bool enabled);
|
||||
/// Enables relative mouse mode in the host, and/or hides the cursor.
|
||||
void SetMouseMode(bool relative_mode, bool hide_cursor);
|
||||
} // namespace Host
|
||||
|
|
|
@ -95,7 +95,7 @@ void Host::OnInputDeviceDisconnected(const std::string_view& identifier)
|
|||
{
|
||||
}
|
||||
|
||||
void Host::SetRelativeMouseMode(bool enabled)
|
||||
void Host::SetMouseMode(bool relative_mode, bool hide_cursor)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue