GPUDevice: Move software cursor to ImGuiManager
This commit is contained in:
parent
dd01c75f26
commit
259193a8e5
|
@ -69,7 +69,7 @@ std::optional<u32> Controller::GetAnalogInputBytes() const
|
||||||
|
|
||||||
void Controller::LoadSettings(SettingsInterface& si, const char* section) {}
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ public:
|
||||||
virtual void LoadSettings(SettingsInterface& si, const char* section);
|
virtual void LoadSettings(SettingsInterface& si, const char* section);
|
||||||
|
|
||||||
/// Returns the software cursor to use for this controller, if any.
|
/// 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.
|
/// Creates a new controller of the specified type.
|
||||||
static std::unique_ptr<Controller> Create(ControllerType type, u32 index);
|
static std::unique_ptr<Controller> Create(ControllerType type, u32 index);
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#include "guncon.h"
|
#include "guncon.h"
|
||||||
#include "gpu.h"
|
#include "gpu.h"
|
||||||
#include "host.h"
|
#include "host.h"
|
||||||
#include "resources.h"
|
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
|
|
||||||
#include "util/gpu_device.h"
|
#include "util/gpu_device.h"
|
||||||
|
@ -12,6 +11,7 @@
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
|
#include "common/path.h"
|
||||||
|
|
||||||
#include <array>
|
#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}};
|
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;
|
GunCon::~GunCon() = default;
|
||||||
|
|
||||||
|
@ -248,22 +250,10 @@ void GunCon::LoadSettings(SettingsInterface& si, const char* section)
|
||||||
{
|
{
|
||||||
Controller::LoadSettings(si, section);
|
Controller::LoadSettings(si, section);
|
||||||
|
|
||||||
std::string path = si.GetStringValue(section, "CrosshairImagePath");
|
m_crosshair_image_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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef __ANDROID__
|
#ifndef __ANDROID__
|
||||||
if (!m_crosshair_image.IsValid())
|
if (m_crosshair_image_path.empty())
|
||||||
{
|
m_crosshair_image_path = Path::Combine(EmuFolders::Resources, "images/crosshair.png");
|
||||||
m_crosshair_image.SetPixels(Resources::CROSSHAIR_IMAGE_WIDTH, Resources::CROSSHAIR_IMAGE_HEIGHT,
|
|
||||||
Resources::CROSSHAIR_IMAGE_DATA.data());
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_crosshair_image_scale = si.GetFloatValue(section, "CrosshairScale", 1.0f);
|
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);
|
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;
|
return false;
|
||||||
|
|
||||||
*image = &m_crosshair_image;
|
*image_path = m_crosshair_image_path;
|
||||||
*image_scale = m_crosshair_image_scale;
|
*image_scale = m_crosshair_image_scale;
|
||||||
*relative_mode = false;
|
*relative_mode = false;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -32,7 +32,7 @@ public:
|
||||||
bool DoState(StateWrapper& sw, bool apply_input_state) override;
|
bool DoState(StateWrapper& sw, bool apply_input_state) override;
|
||||||
|
|
||||||
void LoadSettings(SettingsInterface& si, const char* section) 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;
|
float GetBindState(u32 index) const override;
|
||||||
void SetBindState(u32 index, float value) override;
|
void SetBindState(u32 index, float value) override;
|
||||||
|
@ -56,7 +56,6 @@ private:
|
||||||
YMSB
|
YMSB
|
||||||
};
|
};
|
||||||
|
|
||||||
Common::RGBA8Image m_crosshair_image;
|
|
||||||
std::string m_crosshair_image_path;
|
std::string m_crosshair_image_path;
|
||||||
float m_crosshair_image_scale = 1.0f;
|
float m_crosshair_image_scale = 1.0f;
|
||||||
float m_x_scale = 1.0f;
|
float m_x_scale = 1.0f;
|
||||||
|
|
|
@ -349,6 +349,7 @@ void Host::RenderDisplay(bool skip_present)
|
||||||
FullscreenUI::Render();
|
FullscreenUI::Render();
|
||||||
ImGuiManager::RenderTextOverlays();
|
ImGuiManager::RenderTextOverlays();
|
||||||
ImGuiManager::RenderOSDMessages();
|
ImGuiManager::RenderOSDMessages();
|
||||||
|
ImGuiManager::RenderSoftwareCursors();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug windows are always rendered, otherwise mouse input breaks on skip.
|
// Debug windows are always rendered, otherwise mouse input breaks on skip.
|
||||||
|
|
|
@ -215,7 +215,7 @@ void PlayStationMouse::LoadSettings(SettingsInterface& si, const char* section)
|
||||||
m_use_relative_mode = si.GetBoolValue(section, "RelativeMouseMode", false);
|
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;
|
*relative_mode = m_use_relative_mode;
|
||||||
return m_use_relative_mode;
|
return m_use_relative_mode;
|
||||||
|
|
|
@ -36,7 +36,7 @@ public:
|
||||||
bool Transfer(const u8 data_in, u8* data_out) override;
|
bool Transfer(const u8 data_in, u8* data_out) override;
|
||||||
|
|
||||||
void LoadSettings(SettingsInterface& si, const char* section) 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:
|
private:
|
||||||
void UpdatePosition();
|
void UpdatePosition();
|
||||||
|
|
|
@ -4624,11 +4624,11 @@ void System::UpdateSoftwareCursor()
|
||||||
if (!IsValid())
|
if (!IsValid())
|
||||||
{
|
{
|
||||||
Host::SetMouseMode(false, false);
|
Host::SetMouseMode(false, false);
|
||||||
g_gpu_device->ClearSoftwareCursor();
|
ImGuiManager::ClearSoftwareCursor(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Common::RGBA8Image* image = nullptr;
|
std::string image_path;
|
||||||
float image_scale = 1.0f;
|
float image_scale = 1.0f;
|
||||||
bool relative_mode = false;
|
bool relative_mode = false;
|
||||||
bool hide_cursor = false;
|
bool hide_cursor = false;
|
||||||
|
@ -4636,7 +4636,7 @@ void System::UpdateSoftwareCursor()
|
||||||
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
|
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
|
||||||
{
|
{
|
||||||
Controller* controller = System::GetController(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;
|
hide_cursor = true;
|
||||||
break;
|
break;
|
||||||
|
@ -4645,15 +4645,10 @@ void System::UpdateSoftwareCursor()
|
||||||
|
|
||||||
Host::SetMouseMode(relative_mode, hide_cursor);
|
Host::SetMouseMode(relative_mode, hide_cursor);
|
||||||
|
|
||||||
if (image && image->IsValid())
|
if (!image_path.empty())
|
||||||
{
|
ImGuiManager::SetSoftwareCursor(0, std::move(image_path), image_scale);
|
||||||
g_gpu_device->SetSoftwareCursor(image->GetPixels(), image->GetWidth(), image->GetHeight(), image->GetPitch(),
|
|
||||||
image_scale);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
ImGuiManager::ClearSoftwareCursor(0);
|
||||||
g_gpu_device->ClearSoftwareCursor();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::RequestDisplaySize(float scale /*= 0.0f*/)
|
void System::RequestDisplaySize(float scale /*= 0.0f*/)
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
|
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "stb_image.h"
|
|
||||||
#include "stb_image_resize.h"
|
#include "stb_image_resize.h"
|
||||||
#include "stb_image_write.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_vs = CreateShader(GPUShaderStage::Vertex, shadergen.GenerateDisplayVertexShader());
|
||||||
std::unique_ptr<GPUShader> display_fs =
|
std::unique_ptr<GPUShader> display_fs =
|
||||||
CreateShader(GPUShaderStage::Fragment, shadergen.GenerateDisplayFragmentShader(true));
|
CreateShader(GPUShaderStage::Fragment, shadergen.GenerateDisplayFragmentShader(true));
|
||||||
std::unique_ptr<GPUShader> cursor_fs =
|
if (!display_vs || !display_fs)
|
||||||
CreateShader(GPUShaderStage::Fragment, shadergen.GenerateDisplayFragmentShader(false));
|
|
||||||
if (!display_vs || !display_fs || !cursor_fs)
|
|
||||||
return false;
|
return false;
|
||||||
GL_OBJECT_NAME(display_vs, "Display Vertex Shader");
|
GL_OBJECT_NAME(display_vs, "Display Vertex Shader");
|
||||||
GL_OBJECT_NAME(display_fs, "Display Fragment Shader");
|
GL_OBJECT_NAME(display_fs, "Display Fragment Shader");
|
||||||
GL_OBJECT_NAME(cursor_fs, "Cursor Fragment Shader");
|
|
||||||
|
|
||||||
plconfig.vertex_shader = display_vs.get();
|
plconfig.vertex_shader = display_vs.get();
|
||||||
plconfig.fragment_shader = display_fs.get();
|
plconfig.fragment_shader = display_fs.get();
|
||||||
|
@ -446,12 +442,6 @@ bool GPUDevice::CreateResources()
|
||||||
return false;
|
return false;
|
||||||
GL_OBJECT_NAME(m_display_pipeline, "Display Pipeline");
|
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_vs = CreateShader(GPUShaderStage::Vertex, shadergen.GenerateImGuiVertexShader());
|
||||||
std::unique_ptr<GPUShader> imgui_fs = CreateShader(GPUShaderStage::Fragment, shadergen.GenerateImGuiFragmentShader());
|
std::unique_ptr<GPUShader> imgui_fs = CreateShader(GPUShaderStage::Fragment, shadergen.GenerateImGuiFragmentShader());
|
||||||
if (!imgui_vs || !imgui_fs)
|
if (!imgui_vs || !imgui_fs)
|
||||||
|
@ -472,6 +462,7 @@ bool GPUDevice::CreateResources()
|
||||||
plconfig.input_layout.vertex_stride = sizeof(ImDrawVert);
|
plconfig.input_layout.vertex_stride = sizeof(ImDrawVert);
|
||||||
plconfig.vertex_shader = imgui_vs.get();
|
plconfig.vertex_shader = imgui_vs.get();
|
||||||
plconfig.fragment_shader = imgui_fs.get();
|
plconfig.fragment_shader = imgui_fs.get();
|
||||||
|
plconfig.blend = GPUPipeline::BlendState::GetAlphaBlendingState();
|
||||||
|
|
||||||
m_imgui_pipeline = CreatePipeline(plconfig);
|
m_imgui_pipeline = CreatePipeline(plconfig);
|
||||||
if (!m_imgui_pipeline)
|
if (!m_imgui_pipeline)
|
||||||
|
@ -486,12 +477,9 @@ bool GPUDevice::CreateResources()
|
||||||
|
|
||||||
void GPUDevice::DestroyResources()
|
void GPUDevice::DestroyResources()
|
||||||
{
|
{
|
||||||
m_cursor_texture.reset();
|
|
||||||
|
|
||||||
m_imgui_font_texture.reset();
|
m_imgui_font_texture.reset();
|
||||||
m_imgui_pipeline.reset();
|
m_imgui_pipeline.reset();
|
||||||
|
|
||||||
m_cursor_pipeline.reset();
|
|
||||||
m_display_pipeline.reset();
|
m_display_pipeline.reset();
|
||||||
m_imgui_pipeline.reset();
|
m_imgui_pipeline.reset();
|
||||||
|
|
||||||
|
@ -862,61 +850,6 @@ float GPUDevice::GetAndResetAccumulatedGPUTime()
|
||||||
return 0.0f;
|
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
|
bool GPUDevice::IsUsingLinearFiltering() const
|
||||||
{
|
{
|
||||||
return g_settings.display_linear_filtering;
|
return g_settings.display_linear_filtering;
|
||||||
|
@ -962,7 +895,6 @@ bool GPUDevice::Render(bool skip_present)
|
||||||
SetViewportAndScissor(0, 0, GetWindowWidth(), GetWindowHeight());
|
SetViewportAndScissor(0, 0, GetWindowWidth(), GetWindowHeight());
|
||||||
|
|
||||||
RenderImGui();
|
RenderImGui();
|
||||||
RenderSoftwareCursor();
|
|
||||||
|
|
||||||
EndPresent();
|
EndPresent();
|
||||||
return true;
|
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,
|
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_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
|
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));
|
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,
|
std::tuple<float, float> GPUDevice::ConvertWindowCoordinatesToDisplayCoordinates(s32 window_x, s32 window_y,
|
||||||
s32 window_width,
|
s32 window_width,
|
||||||
s32 window_height) const
|
s32 window_height) const
|
||||||
|
|
|
@ -638,18 +638,6 @@ public:
|
||||||
/// Returns the amount of GPU time utilized since the last time this method was called.
|
/// Returns the amount of GPU time utilized since the last time this method was called.
|
||||||
virtual float GetAndResetAccumulatedGPUTime();
|
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.
|
/// Helper function for computing the draw rectangle in a larger window.
|
||||||
std::tuple<s32, s32, s32, s32> CalculateDrawRect(s32 window_width, s32 window_height,
|
std::tuple<s32, s32, s32, s32> CalculateDrawRect(s32 window_width, s32 window_height,
|
||||||
bool apply_aspect_ratio = true) const;
|
bool apply_aspect_ratio = true) const;
|
||||||
|
@ -705,7 +693,6 @@ protected:
|
||||||
bool m_debug_device = false;
|
bool m_debug_device = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ALWAYS_INLINE bool HasSoftwareCursor() const { return static_cast<bool>(m_cursor_texture); }
|
|
||||||
ALWAYS_INLINE bool HasDisplayTexture() const { return (m_display_texture != nullptr); }
|
ALWAYS_INLINE bool HasDisplayTexture() const { return (m_display_texture != nullptr); }
|
||||||
|
|
||||||
void OpenShaderCache(const std::string_view& base_path, u32 version);
|
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_height, float* out_left_padding, float* out_top_padding, float* out_scale,
|
||||||
float* out_x_scale, bool apply_aspect_ratio = true) const;
|
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 RenderImGui();
|
||||||
|
|
||||||
void RenderSoftwareCursor();
|
|
||||||
|
|
||||||
bool RenderDisplay(GPUFramebuffer* target, s32 left, s32 top, s32 width, s32 height, GPUTexture* texture,
|
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,
|
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
|
||||||
bool linear_filter);
|
bool linear_filter);
|
||||||
void RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, GPUTexture* texture);
|
|
||||||
|
|
||||||
u64 m_last_frame_displayed_time = 0;
|
u64 m_last_frame_displayed_time = 0;
|
||||||
|
|
||||||
|
@ -755,10 +736,6 @@ private:
|
||||||
std::unique_ptr<GPUPipeline> m_imgui_pipeline;
|
std::unique_ptr<GPUPipeline> m_imgui_pipeline;
|
||||||
std::unique_ptr<GPUTexture> m_imgui_font_texture;
|
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;
|
bool m_display_changed = false;
|
||||||
|
|
||||||
std::unique_ptr<PostProcessingChain> m_post_processing_chain;
|
std::unique_ptr<PostProcessingChain> m_post_processing_chain;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/file_system.h"
|
#include "common/file_system.h"
|
||||||
|
#include "common/image.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "common/timer.h"
|
#include "common/timer.h"
|
||||||
|
@ -28,6 +29,17 @@
|
||||||
Log_SetChannel(ImGuiManager);
|
Log_SetChannel(ImGuiManager);
|
||||||
|
|
||||||
namespace 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 SetStyle();
|
||||||
static void SetKeyMap();
|
static void SetKeyMap();
|
||||||
static bool LoadFontData();
|
static bool LoadFontData();
|
||||||
|
@ -37,6 +49,10 @@ static ImFont* AddFixedFont(float size);
|
||||||
static bool AddIconFonts(float size);
|
static bool AddIconFonts(float size);
|
||||||
static void AcquirePendingOSDMessages();
|
static void AcquirePendingOSDMessages();
|
||||||
static void DrawOSDMessages();
|
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
|
} // namespace ImGuiManager
|
||||||
|
|
||||||
static float s_global_prescale = 1.0f; // before window scale
|
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
|
// mapping of host key -> imgui key
|
||||||
static std::unordered_map<u32, ImGuiKey> s_imgui_key_map;
|
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)
|
void ImGuiManager::SetFontPath(std::string path)
|
||||||
{
|
{
|
||||||
s_font_path = std::move(path);
|
s_font_path = std::move(path);
|
||||||
|
@ -125,11 +155,15 @@ bool ImGuiManager::Initialize(float global_scale)
|
||||||
ImGui::GetIO().Fonts->ClearTexData();
|
ImGui::GetIO().Fonts->ClearTexData();
|
||||||
|
|
||||||
NewFrame();
|
NewFrame();
|
||||||
|
|
||||||
|
CreateSoftwareCursorTextures();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiManager::Shutdown()
|
void ImGuiManager::Shutdown()
|
||||||
{
|
{
|
||||||
|
DestroySoftwareCursorTextures();
|
||||||
|
|
||||||
if (ImGui::GetCurrentContext())
|
if (ImGui::GetCurrentContext())
|
||||||
ImGui::DestroyContext();
|
ImGui::DestroyContext();
|
||||||
|
|
||||||
|
@ -541,18 +575,6 @@ bool ImGuiManager::HasFullscreenFonts()
|
||||||
return (s_medium_font && s_large_font);
|
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*/)
|
void Host::AddOSDMessage(std::string message, float duration /*= 2.0f*/)
|
||||||
{
|
{
|
||||||
AddKeyedOSDMessage(std::string(), std::move(message), duration);
|
AddKeyedOSDMessage(std::string(), std::move(message), duration);
|
||||||
|
@ -634,9 +656,9 @@ void ImGuiManager::AcquirePendingOSDMessages()
|
||||||
OSDMessage& new_msg = s_osd_posted_messages.front();
|
OSDMessage& new_msg = s_osd_posted_messages.front();
|
||||||
std::deque<OSDMessage>::iterator iter;
|
std::deque<OSDMessage>::iterator iter;
|
||||||
if (!new_msg.key.empty() && (iter = std::find_if(s_osd_active_messages.begin(), s_osd_active_messages.end(),
|
if (!new_msg.key.empty() && (iter = std::find_if(s_osd_active_messages.begin(), s_osd_active_messages.end(),
|
||||||
[&new_msg](const OSDMessage& other) {
|
[&new_msg](const OSDMessage& other) {
|
||||||
return new_msg.key == other.key;
|
return new_msg.key == other.key;
|
||||||
})) != s_osd_active_messages.end())
|
})) != s_osd_active_messages.end())
|
||||||
{
|
{
|
||||||
iter->text = std::move(new_msg.text);
|
iter->text = std::move(new_msg.text);
|
||||||
iter->duration = new_msg.duration;
|
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);
|
ImGui::GetIO().AddKeyAnalogEvent(key_map[static_cast<u32>(key)], (value > 0.0f), value);
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -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.
|
/// Called on the CPU thread when any input event fires. Allows imgui to take over controller navigation.
|
||||||
bool ProcessGenericInputEvent(GenericInputBinding key, float value);
|
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 ImGuiManager
|
||||||
|
|
||||||
namespace Host {
|
namespace Host {
|
||||||
|
|
|
@ -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)
|
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);
|
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);
|
std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputManager::UpdateHostMouseMode()
|
||||||
|
{
|
||||||
|
// TODO: Move from System to here.
|
||||||
|
}
|
||||||
|
|
||||||
bool InputManager::IsUsingRawInput()
|
bool InputManager::IsUsingRawInput()
|
||||||
{
|
{
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
|
|
@ -174,6 +174,11 @@ static constexpr double VIBRATION_UPDATE_INTERVAL_SECONDS = 0.5; // 500ms
|
||||||
|
|
||||||
/// Maximum number of host mouse devices.
|
/// Maximum number of host mouse devices.
|
||||||
static constexpr u32 MAX_POINTER_DEVICES = 1;
|
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.
|
/// Number of macro buttons per controller.
|
||||||
static constexpr u32 NUM_MACRO_BUTTONS_PER_CONTROLLER = 4;
|
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.
|
/// The pad vibration state will internally remain, so that when emulation is unpaused, the effect resumes.
|
||||||
void PauseVibration();
|
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.
|
/// 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);
|
void UpdatePointerAbsolutePosition(u32 index, float x, float y);
|
||||||
|
|
||||||
|
@ -309,6 +317,9 @@ void UpdatePointerAbsolutePosition(u32 index, float x, float y);
|
||||||
/// reporting.
|
/// reporting.
|
||||||
void UpdatePointerRelativeDelta(u32 index, InputPointerAxis axis, float d, bool raw_input = false);
|
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.
|
/// Sets the state of the specified macro button.
|
||||||
void SetMacroButtonState(u32 pad, u32 index, bool state);
|
void SetMacroButtonState(u32 pad, u32 index, bool state);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue