GPUDevice: Move software cursor to ImGuiManager

This commit is contained in:
Stenzek 2023-08-27 16:22:45 +10:00
parent dd01c75f26
commit 259193a8e5
14 changed files with 201 additions and 185 deletions

View file

@ -69,7 +69,7 @@ std::optional<u32> Controller::GetAnalogInputBytes() const
void Controller::LoadSettings(SettingsInterface& si, const char* section) {}
bool Controller::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode)
bool Controller::GetSoftwareCursor(std::string* image_path, float* image_scale, bool* relative_mode)
{
return false;
}

View file

@ -88,7 +88,7 @@ public:
virtual void LoadSettings(SettingsInterface& si, const char* section);
/// Returns the software cursor to use for this controller, if any.
virtual bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode);
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

@ -4,7 +4,6 @@
#include "guncon.h"
#include "gpu.h"
#include "host.h"
#include "resources.h"
#include "system.h"
#include "util/gpu_device.h"
@ -12,6 +11,7 @@
#include "common/assert.h"
#include "common/log.h"
#include "common/path.h"
#include <array>
@ -19,7 +19,9 @@ Log_SetChannel(GunCon);
static constexpr std::array<u8, static_cast<size_t>(GunCon::Button::Count)> s_button_indices = {{13, 3, 14}};
GunCon::GunCon(u32 index) : Controller(index) {}
GunCon::GunCon(u32 index) : Controller(index)
{
}
GunCon::~GunCon() = default;
@ -248,22 +250,10 @@ void GunCon::LoadSettings(SettingsInterface& si, const char* section)
{
Controller::LoadSettings(si, section);
std::string path = si.GetStringValue(section, "CrosshairImagePath");
if (path != m_crosshair_image_path)
{
m_crosshair_image_path = std::move(path);
if (m_crosshair_image_path.empty() || !m_crosshair_image.LoadFromFile(m_crosshair_image_path.c_str()))
{
m_crosshair_image.Invalidate();
}
}
m_crosshair_image_path = si.GetStringValue(section, "CrosshairImagePath");
#ifndef __ANDROID__
if (!m_crosshair_image.IsValid())
{
m_crosshair_image.SetPixels(Resources::CROSSHAIR_IMAGE_WIDTH, Resources::CROSSHAIR_IMAGE_HEIGHT,
Resources::CROSSHAIR_IMAGE_DATA.data());
}
if (m_crosshair_image_path.empty())
m_crosshair_image_path = Path::Combine(EmuFolders::Resources, "images/crosshair.png");
#endif
m_crosshair_image_scale = si.GetFloatValue(section, "CrosshairScale", 1.0f);
@ -271,12 +261,12 @@ void GunCon::LoadSettings(SettingsInterface& si, const char* section)
m_x_scale = si.GetFloatValue(section, "XScale", 1.0f);
}
bool GunCon::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode)
bool GunCon::GetSoftwareCursor(std::string* image_path, float* image_scale, bool* relative_mode)
{
if (!m_crosshair_image.IsValid())
if (m_crosshair_image_path.empty())
return false;
*image = &m_crosshair_image;
*image_path = m_crosshair_image_path;
*image_scale = m_crosshair_image_scale;
*relative_mode = false;
return true;

View file

@ -32,7 +32,7 @@ public:
bool DoState(StateWrapper& sw, bool apply_input_state) override;
void LoadSettings(SettingsInterface& si, const char* section) override;
bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode) 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;
@ -56,7 +56,6 @@ private:
YMSB
};
Common::RGBA8Image m_crosshair_image;
std::string m_crosshair_image_path;
float m_crosshair_image_scale = 1.0f;
float m_x_scale = 1.0f;

View file

@ -349,6 +349,7 @@ void Host::RenderDisplay(bool skip_present)
FullscreenUI::Render();
ImGuiManager::RenderTextOverlays();
ImGuiManager::RenderOSDMessages();
ImGuiManager::RenderSoftwareCursors();
}
// Debug windows are always rendered, otherwise mouse input breaks on skip.

View file

@ -215,7 +215,7 @@ void PlayStationMouse::LoadSettings(SettingsInterface& si, const char* section)
m_use_relative_mode = si.GetBoolValue(section, "RelativeMouseMode", false);
}
bool PlayStationMouse::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode)
bool PlayStationMouse::GetSoftwareCursor(std::string* image_path, float* image_scale, bool* relative_mode)
{
*relative_mode = m_use_relative_mode;
return m_use_relative_mode;

View file

@ -36,7 +36,7 @@ public:
bool Transfer(const u8 data_in, u8* data_out) override;
void LoadSettings(SettingsInterface& si, const char* section) override;
bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode) override;
bool GetSoftwareCursor(std::string* image_path, float* image_scale, bool* relative_mode) override;
private:
void UpdatePosition();

View file

@ -4624,11 +4624,11 @@ void System::UpdateSoftwareCursor()
if (!IsValid())
{
Host::SetMouseMode(false, false);
g_gpu_device->ClearSoftwareCursor();
ImGuiManager::ClearSoftwareCursor(0);
return;
}
const Common::RGBA8Image* image = nullptr;
std::string image_path;
float image_scale = 1.0f;
bool relative_mode = false;
bool hide_cursor = false;
@ -4636,7 +4636,7 @@ void System::UpdateSoftwareCursor()
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{
Controller* controller = System::GetController(i);
if (controller && controller->GetSoftwareCursor(&image, &image_scale, &relative_mode))
if (controller && controller->GetSoftwareCursor(&image_path, &image_scale, &relative_mode))
{
hide_cursor = true;
break;
@ -4645,15 +4645,10 @@ void System::UpdateSoftwareCursor()
Host::SetMouseMode(relative_mode, hide_cursor);
if (image && image->IsValid())
{
g_gpu_device->SetSoftwareCursor(image->GetPixels(), image->GetWidth(), image->GetHeight(), image->GetPitch(),
image_scale);
}
if (!image_path.empty())
ImGuiManager::SetSoftwareCursor(0, std::move(image_path), image_scale);
else
{
g_gpu_device->ClearSoftwareCursor();
}
ImGuiManager::ClearSoftwareCursor(0);
}
void System::RequestDisplaySize(float scale /*= 0.0f*/)

View file

@ -20,7 +20,6 @@
#include "fmt/format.h"
#include "imgui.h"
#include "stb_image.h"
#include "stb_image_resize.h"
#include "stb_image_write.h"
@ -432,13 +431,10 @@ bool GPUDevice::CreateResources()
std::unique_ptr<GPUShader> display_vs = CreateShader(GPUShaderStage::Vertex, shadergen.GenerateDisplayVertexShader());
std::unique_ptr<GPUShader> display_fs =
CreateShader(GPUShaderStage::Fragment, shadergen.GenerateDisplayFragmentShader(true));
std::unique_ptr<GPUShader> cursor_fs =
CreateShader(GPUShaderStage::Fragment, shadergen.GenerateDisplayFragmentShader(false));
if (!display_vs || !display_fs || !cursor_fs)
if (!display_vs || !display_fs)
return false;
GL_OBJECT_NAME(display_vs, "Display Vertex Shader");
GL_OBJECT_NAME(display_fs, "Display Fragment Shader");
GL_OBJECT_NAME(cursor_fs, "Cursor Fragment Shader");
plconfig.vertex_shader = display_vs.get();
plconfig.fragment_shader = display_fs.get();
@ -446,12 +442,6 @@ bool GPUDevice::CreateResources()
return false;
GL_OBJECT_NAME(m_display_pipeline, "Display Pipeline");
plconfig.blend = GPUPipeline::BlendState::GetAlphaBlendingState();
plconfig.fragment_shader = cursor_fs.get();
if (!(m_cursor_pipeline = CreatePipeline(plconfig)))
return false;
GL_OBJECT_NAME(m_cursor_pipeline, "Cursor Pipeline");
std::unique_ptr<GPUShader> imgui_vs = CreateShader(GPUShaderStage::Vertex, shadergen.GenerateImGuiVertexShader());
std::unique_ptr<GPUShader> imgui_fs = CreateShader(GPUShaderStage::Fragment, shadergen.GenerateImGuiFragmentShader());
if (!imgui_vs || !imgui_fs)
@ -472,6 +462,7 @@ bool GPUDevice::CreateResources()
plconfig.input_layout.vertex_stride = sizeof(ImDrawVert);
plconfig.vertex_shader = imgui_vs.get();
plconfig.fragment_shader = imgui_fs.get();
plconfig.blend = GPUPipeline::BlendState::GetAlphaBlendingState();
m_imgui_pipeline = CreatePipeline(plconfig);
if (!m_imgui_pipeline)
@ -486,12 +477,9 @@ bool GPUDevice::CreateResources()
void GPUDevice::DestroyResources()
{
m_cursor_texture.reset();
m_imgui_font_texture.reset();
m_imgui_pipeline.reset();
m_cursor_pipeline.reset();
m_display_pipeline.reset();
m_imgui_pipeline.reset();
@ -862,61 +850,6 @@ float GPUDevice::GetAndResetAccumulatedGPUTime()
return 0.0f;
}
void GPUDevice::SetSoftwareCursor(std::unique_ptr<GPUTexture> texture, float scale /*= 1.0f*/)
{
if (texture)
texture->MakeReadyForSampling();
m_cursor_texture = std::move(texture);
m_cursor_texture_scale = scale;
}
bool GPUDevice::SetSoftwareCursor(const void* pixels, u32 width, u32 height, u32 stride, float scale /*= 1.0f*/)
{
std::unique_ptr<GPUTexture> tex =
CreateTexture(width, height, 1, 1, 1, GPUTexture::Type::Texture, GPUTexture::Format::RGBA8, pixels, stride, false);
if (!tex)
return false;
SetSoftwareCursor(std::move(tex), scale);
return true;
}
bool GPUDevice::SetSoftwareCursor(const char* path, float scale /*= 1.0f*/)
{
auto fp = FileSystem::OpenManagedCFile(path, "rb");
if (!fp)
{
return false;
}
int width, height, file_channels;
u8* pixel_data = stbi_load_from_file(fp.get(), &width, &height, &file_channels, 4);
if (!pixel_data)
{
const char* error_reason = stbi_failure_reason();
Log_ErrorPrintf("Failed to load image from '%s': %s", path, error_reason ? error_reason : "unknown error");
return false;
}
std::unique_ptr<GPUTexture> tex =
CreateTexture(static_cast<u32>(width), static_cast<u32>(height), 1, 1, 1, GPUTexture::Type::Texture,
GPUTexture::Format::RGBA8, pixel_data, sizeof(u32) * static_cast<u32>(width), false);
stbi_image_free(pixel_data);
if (!tex)
return false;
Log_InfoPrintf("Loaded %dx%d image from '%s' for software cursor", width, height, path);
SetSoftwareCursor(std::move(tex), scale);
return true;
}
void GPUDevice::ClearSoftwareCursor()
{
m_cursor_texture.reset();
m_cursor_texture_scale = 1.0f;
}
bool GPUDevice::IsUsingLinearFiltering() const
{
return g_settings.display_linear_filtering;
@ -962,7 +895,6 @@ bool GPUDevice::Render(bool skip_present)
SetViewportAndScissor(0, 0, GetWindowWidth(), GetWindowHeight());
RenderImGui();
RenderSoftwareCursor();
EndPresent();
return true;
@ -1054,27 +986,6 @@ bool GPUDevice::RenderDisplay(GPUFramebuffer* target, s32 left, s32 top, s32 wid
}
}
void GPUDevice::RenderSoftwareCursor()
{
if (!HasSoftwareCursor())
return;
const auto [left, top, width, height] = CalculateSoftwareCursorDrawRect();
RenderSoftwareCursor(left, top, width, height, m_cursor_texture.get());
}
void GPUDevice::RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, GPUTexture* texture)
{
SetPipeline(m_display_pipeline.get());
SetTextureSampler(0, texture, m_linear_sampler.get());
const float uniforms[4] = {0.0f, 0.0f, 1.0f, 1.0f};
PushUniformBuffer(uniforms, sizeof(uniforms));
SetViewportAndScissor(left, top, width, height);
Draw(3, 0);
}
void GPUDevice::CalculateDrawRect(s32 window_width, s32 window_height, float* out_left, float* out_top,
float* out_width, float* out_height, float* out_left_padding, float* out_top_padding,
float* out_scale, float* out_x_scale, bool apply_aspect_ratio /* = true */) const
@ -1193,25 +1104,6 @@ std::tuple<s32, s32, s32, s32> GPUDevice::CalculateDrawRect(s32 window_width, s3
static_cast<s32>(width), static_cast<s32>(height));
}
std::tuple<s32, s32, s32, s32> GPUDevice::CalculateSoftwareCursorDrawRect() const
{
return CalculateSoftwareCursorDrawRect(m_mouse_position_x, m_mouse_position_y);
}
std::tuple<s32, s32, s32, s32> GPUDevice::CalculateSoftwareCursorDrawRect(s32 cursor_x, s32 cursor_y) const
{
const float scale = m_window_info.surface_scale * m_cursor_texture_scale;
const u32 cursor_extents_x = static_cast<u32>(static_cast<float>(m_cursor_texture->GetWidth()) * scale * 0.5f);
const u32 cursor_extents_y = static_cast<u32>(static_cast<float>(m_cursor_texture->GetHeight()) * scale * 0.5f);
const s32 out_left = cursor_x - cursor_extents_x;
const s32 out_top = cursor_y - cursor_extents_y;
const s32 out_width = cursor_extents_x * 2u;
const s32 out_height = cursor_extents_y * 2u;
return std::tie(out_left, out_top, out_width, out_height);
}
std::tuple<float, float> GPUDevice::ConvertWindowCoordinatesToDisplayCoordinates(s32 window_x, s32 window_y,
s32 window_width,
s32 window_height) const

View file

@ -638,18 +638,6 @@ public:
/// Returns the amount of GPU time utilized since the last time this method was called.
virtual float GetAndResetAccumulatedGPUTime();
/// Sets the software cursor to the specified texture. Ownership of the texture is transferred.
void SetSoftwareCursor(std::unique_ptr<GPUTexture> texture, float scale = 1.0f);
/// Sets the software cursor to the specified image.
bool SetSoftwareCursor(const void* pixels, u32 width, u32 height, u32 stride, float scale = 1.0f);
/// Sets the software cursor to the specified path (png image).
bool SetSoftwareCursor(const char* path, float scale = 1.0f);
/// Disables the software cursor.
void ClearSoftwareCursor();
/// Helper function for computing the draw rectangle in a larger window.
std::tuple<s32, s32, s32, s32> CalculateDrawRect(s32 window_width, s32 window_height,
bool apply_aspect_ratio = true) const;
@ -705,7 +693,6 @@ protected:
bool m_debug_device = false;
private:
ALWAYS_INLINE bool HasSoftwareCursor() const { return static_cast<bool>(m_cursor_texture); }
ALWAYS_INLINE bool HasDisplayTexture() const { return (m_display_texture != nullptr); }
void OpenShaderCache(const std::string_view& base_path, u32 version);
@ -719,17 +706,11 @@ private:
float* out_height, float* out_left_padding, float* out_top_padding, float* out_scale,
float* out_x_scale, bool apply_aspect_ratio = true) const;
std::tuple<s32, s32, s32, s32> CalculateSoftwareCursorDrawRect() const;
std::tuple<s32, s32, s32, s32> CalculateSoftwareCursorDrawRect(s32 cursor_x, s32 cursor_y) const;
void RenderImGui();
void RenderSoftwareCursor();
bool RenderDisplay(GPUFramebuffer* target, s32 left, s32 top, s32 width, s32 height, GPUTexture* texture,
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
bool linear_filter);
void RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, GPUTexture* texture);
u64 m_last_frame_displayed_time = 0;
@ -755,10 +736,6 @@ private:
std::unique_ptr<GPUPipeline> m_imgui_pipeline;
std::unique_ptr<GPUTexture> m_imgui_font_texture;
std::unique_ptr<GPUPipeline> m_cursor_pipeline;
std::unique_ptr<GPUTexture> m_cursor_texture;
float m_cursor_texture_scale = 1.0f;
bool m_display_changed = false;
std::unique_ptr<PostProcessingChain> m_post_processing_chain;

View file

@ -9,6 +9,7 @@
#include "common/assert.h"
#include "common/file_system.h"
#include "common/image.h"
#include "common/log.h"
#include "common/string_util.h"
#include "common/timer.h"
@ -28,6 +29,17 @@
Log_SetChannel(ImGuiManager);
namespace ImGuiManager {
struct SoftwareCursor
{
std::string image_path;
std::unique_ptr<GPUTexture> 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();
@ -37,6 +49,10 @@ static ImFont* AddFixedFont(float size);
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);
} // namespace ImGuiManager
static float s_global_prescale = 1.0f; // before window scale
@ -63,6 +79,20 @@ static std::atomic_bool s_imgui_wants_mouse{false};
// mapping of host key -> imgui key
static std::unordered_map<u32, ImGuiKey> s_imgui_key_map;
struct OSDMessage
{
std::string key;
std::string text;
std::chrono::steady_clock::time_point time;
float duration;
};
static std::deque<OSDMessage> s_osd_active_messages;
static std::deque<OSDMessage> s_osd_posted_messages;
static std::mutex s_osd_messages_lock;
static std::array<ImGuiManager::SoftwareCursor, InputManager::MAX_SOFTWARE_CURSORS> s_software_cursors = {};
void ImGuiManager::SetFontPath(std::string path)
{
s_font_path = std::move(path);
@ -125,11 +155,15 @@ bool ImGuiManager::Initialize(float global_scale)
ImGui::GetIO().Fonts->ClearTexData();
NewFrame();
CreateSoftwareCursorTextures();
return true;
}
void ImGuiManager::Shutdown()
{
DestroySoftwareCursorTextures();
if (ImGui::GetCurrentContext())
ImGui::DestroyContext();
@ -541,18 +575,6 @@ bool ImGuiManager::HasFullscreenFonts()
return (s_medium_font && s_large_font);
}
struct OSDMessage
{
std::string key;
std::string text;
std::chrono::steady_clock::time_point time;
float duration;
};
static std::deque<OSDMessage> s_osd_active_messages;
static std::deque<OSDMessage> s_osd_posted_messages;
static std::mutex s_osd_messages_lock;
void Host::AddOSDMessage(std::string message, float duration /*= 2.0f*/)
{
AddKeyedOSDMessage(std::string(), std::move(message), duration);
@ -634,9 +656,9 @@ void ImGuiManager::AcquirePendingOSDMessages()
OSDMessage& new_msg = s_osd_posted_messages.front();
std::deque<OSDMessage>::iterator iter;
if (!new_msg.key.empty() && (iter = std::find_if(s_osd_active_messages.begin(), s_osd_active_messages.end(),
[&new_msg](const OSDMessage& other) {
return new_msg.key == other.key;
})) != s_osd_active_messages.end())
[&new_msg](const OSDMessage& other) {
return new_msg.key == other.key;
})) != s_osd_active_messages.end())
{
iter->text = std::move(new_msg.text);
iter->duration = new_msg.duration;
@ -842,3 +864,110 @@ bool ImGuiManager::ProcessGenericInputEvent(GenericInputBinding key, float value
ImGui::GetIO().AddKeyAnalogEvent(key_map[static_cast<u32>(key)], (value > 0.0f), 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()))
{
Log_ErrorPrintf("Failed to load software cursor %u image '%s'", index, sc.image_path.c_str());
return;
}
sc.texture = g_gpu_device->CreateTexture(image.GetWidth(), image.GetHeight(), 1, 1, 1, GPUTexture::Type::Texture,
GPUTexture::Format::RGBA8, image.GetPixels(), image.GetPitch());
if (!sc.texture)
{
Log_ErrorPrintf("Failed to upload %ux%u software cursor %u image '%s'", image.GetWidth(), image.GetHeight(), index,
sc.image_path.c_str());
return;
}
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()), min, max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f),
sc.color);
}
void ImGuiManager::RenderSoftwareCursors()
{
// 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)
{
DebugAssert(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 (g_gpu_device)
UpdateSoftwareCursorTexture(index);
// Hide the system cursor when we activate a software cursor.
if (is_hiding_or_showing && index == 0)
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)
{
DebugAssert(index >= InputManager::MAX_POINTER_DEVICES);
SoftwareCursor& sc = s_software_cursors[index];
sc.pos.first = pos_x;
sc.pos.second = pos_y;
}

View file

@ -85,6 +85,17 @@ bool ProcessHostKeyEvent(InputBindingKey key, float value);
/// 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);
/// Adds software cursors to ImGui render list.
void RenderSoftwareCursors();
} // namespace ImGuiManager
namespace Host {

View file

@ -1026,6 +1026,12 @@ void InputManager::GenerateRelativeMouseEvents()
}
}
std::pair<float, float> InputManager::GetPointerAbsolutePosition(u32 index)
{
return std::make_pair(s_host_pointer_positions[index][static_cast<u8>(InputPointerAxis::X)],
s_host_pointer_positions[index][static_cast<u8>(InputPointerAxis::Y)]);
}
void InputManager::UpdatePointerAbsolutePosition(u32 index, float x, float y)
{
const float dx = x - std::exchange(s_host_pointer_positions[index][static_cast<u8>(InputPointerAxis::X)], x);
@ -1046,6 +1052,11 @@ void InputManager::UpdatePointerRelativeDelta(u32 index, InputPointerAxis axis,
std::memory_order_release);
}
void InputManager::UpdateHostMouseMode()
{
// TODO: Move from System to here.
}
bool InputManager::IsUsingRawInput()
{
#if defined(_WIN32)

View file

@ -174,6 +174,11 @@ static constexpr double VIBRATION_UPDATE_INTERVAL_SECONDS = 0.5; // 500ms
/// Maximum number of host mouse devices.
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 controllers with
/// positioning data from the controller instead of a mouse.
static constexpr u32 MAX_SOFTWARE_CURSORS = MAX_POINTER_BUTTONS + 2;
/// Number of macro buttons per controller.
static constexpr u32 NUM_MACRO_BUTTONS_PER_CONTROLLER = 4;
@ -302,6 +307,9 @@ void SetPadVibrationIntensity(u32 pad_index, float large_or_single_motor_intensi
/// The pad vibration state will internally remain, so that when emulation is unpaused, the effect resumes.
void PauseVibration();
/// Reads absolute pointer position.
std::pair<float, float> GetPointerAbsolutePosition(u32 index);
/// Updates absolute pointer position. Can call from UI thread, use when the host only reports absolute coordinates.
void UpdatePointerAbsolutePosition(u32 index, float x, float y);
@ -309,6 +317,9 @@ void UpdatePointerAbsolutePosition(u32 index, float x, float y);
/// reporting.
void UpdatePointerRelativeDelta(u32 index, InputPointerAxis axis, float d, bool raw_input = false);
/// Updates host mouse mode (relative/cursor hiding).
void UpdateHostMouseMode();
/// Sets the state of the specified macro button.
void SetMacroButtonState(u32 pad, u32 index, bool state);