implemented functioning right stick cursor

This commit is contained in:
KojoZero 2026-04-28 02:34:38 -07:00
parent bb5dbf4c0f
commit ae3bd188c0
12 changed files with 44 additions and 39 deletions

View file

@ -2,6 +2,7 @@
#include <cmath> #include <cmath>
#include <algorithm> #include <algorithm>
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/logging/types.h"
#include "core\hle\service\hid\hid.h" #include "core\hle\service\hid\hid.h"
@ -9,10 +10,12 @@ void Cursor::update(){
if (emuWindow != nullptr){ if (emuWindow != nullptr){
stylusInput = Service::HID::Module::getStylusInputs(); stylusInput = Service::HID::Module::getStylusInputs();
modButtons = Service::HID::Module::getModButtons(); modButtons = Service::HID::Module::getModButtons();
setRotation();
if (deviceInUse == 0){ if (deviceInUse == 0){
if (inMacro){ if (inMacro){
runMacro(); runMacro();
} else { } else {
// LOG_INFO(Core, "Stylus X: {:.2f}, Stylus Y: {:.2f}, Stylus Mod: {}, Stylus Touch: {}", stylusInput[0], stylusInput[1], stylusInput[2], stylusInput[3]);
// Reset the cursor position if macro was just played // Reset the cursor position if macro was just played
if (justFinishedMacro > 0){ if (justFinishedMacro > 0){
justFinishedMacro = 0; justFinishedMacro = 0;
@ -47,7 +50,7 @@ void Cursor::update(){
int maxSpeed = 50; int maxSpeed = 50;
float multiplier = 0.5f * pow(4.0f, maxSpeed / 100.0f); // 0 is 0.5x speed, 100 is 2.0x speed. float multiplier = 0.5f * pow(4.0f, maxSpeed / 100.0f); // 0 is 0.5x speed, 100 is 2.0x speed.
float heightSpeed = (240.0f / 33.0f) * multiplier; float heightSpeed = (240.0f / 33.0f) * multiplier;
bool stylusModPressed = stylusInput[3]; bool stylusModPressed = stylusInput[2];
float responsecurve = 175.0f / 100.0f; float responsecurve = 175.0f / 100.0f;
float speedupratio = 400.0f / 100.0f; float speedupratio = 400.0f / 100.0f;
float joystickScaled[2] = {0.0f}; float joystickScaled[2] = {0.0f};
@ -97,10 +100,10 @@ void Cursor::update(){
updateCursorPos(); updateCursorPos();
// Handle stylus touch button presses // Handle stylus touch button presses
if (stylusInput[5]){ if (stylusInput[3]){
touchScreen(); touchScreen();
wasTouching = true; wasTouching = true;
} else if (wasTouching && !stylusInput[5]){ } else if (wasTouching && !stylusInput[3]){
release(); release();
wasTouching = false; wasTouching = false;
} }
@ -111,10 +114,10 @@ void Cursor::update(){
updateCursorPos(); updateCursorPos();
// Handle stylus touch button presses // Handle stylus touch button presses
if (stylusInput[5]){ if (stylusInput[3]){
touchScreen(); touchScreen();
wasTouching = true; wasTouching = true;
} else if (wasTouching && !stylusInput[5]){ } else if (wasTouching && !stylusInput[3]){
release(); release();
wasTouching = false; wasTouching = false;
} }
@ -237,11 +240,12 @@ void Cursor::runMacro(){
} }
} }
void Cursor::setRotation(int rot){ void Cursor::setRotation(){
rotation = rot; if (emuWindow->GetFramebufferLayout().is_portrait){
} rotation = 3;
void Cursor::setLayout(int lay){ } else {
layout = lay; rotation = 0;
}
} }
std::vector<std::array<float, 2>> Cursor::rotateVector(std::vector<std::array<float, 2>> input){ std::vector<std::array<float, 2>> Cursor::rotateVector(std::vector<std::array<float, 2>> input){

View file

@ -15,8 +15,7 @@ public:
void setRawCursorPos(float x, float y); void setRawCursorPos(float x, float y);
void setDeviceInUse(int device); void setDeviceInUse(int device);
void setEmuWindow(Frontend::EmuWindow* emuWindow); void setEmuWindow(Frontend::EmuWindow* emuWindow);
void setRotation(int rot); void setRotation();
void setLayout(int layout);
int cursorPos[2]; int cursorPos[2];
float normStylusDirection[2]; float normStylusDirection[2];
bool cursorEnabled = true; bool cursorEnabled = true;
@ -29,7 +28,7 @@ private:
void updateCursorPos(); void updateCursorPos();
bool wasTouching; bool wasTouching;
std::array<float, 4> stylusInput; std::array<float, 4> stylusInput;
std::array<float, 11> modButtons; std::array<float, 4> modButtons;
void circle(int direction); //0 is clockwise, 1 is counter clockwise void circle(int direction); //0 is clockwise, 1 is counter clockwise
void rub(); void rub();
void runMacro(); void runMacro();
@ -43,7 +42,6 @@ private:
int justFinishedMacro; int justFinishedMacro;
std::array<float, 2> macroInitPos; std::array<float, 2> macroInitPos;
int rotation; int rotation;
int layout;
int deviceInUse; //0 is Gamepad, 1 is Mouse/Tablet int deviceInUse; //0 is Gamepad, 1 is Mouse/Tablet
}; };

View file

@ -193,12 +193,13 @@ bool EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
} }
bool EmuWindow::TouchDirectlyPressed(unsigned internal_x, unsigned internal_y) { bool EmuWindow::TouchDirectlyPressed(unsigned internal_x, unsigned internal_y) {
//This assumes 0, 0 is bottom left corner
std::scoped_lock guard{touch_state->mutex}; std::scoped_lock guard{touch_state->mutex};
std::clamp<unsigned>(internal_x, 0, 319); std::clamp<unsigned>(internal_x, 0, 319);
std::clamp<unsigned>(internal_y, 0, 239); std::clamp<unsigned>(internal_y, 0, 239);
touch_state->touch_pressed = true; touch_state->touch_pressed = true;
touch_state->touch_x = internal_x; touch_state->touch_x = internal_x/319.0f;
touch_state->touch_y = internal_y; touch_state->touch_y = (239-internal_y)/239.0f;
return true; return true;
} }
void EmuWindow::TouchReleased() { void EmuWindow::TouchReleased() {

View file

@ -30,8 +30,8 @@ SERVICE_CONSTRUCT_IMPL(Service::HID::Module)
SERIALIZE_EXPORT_IMPL(Service::HID::Module) SERIALIZE_EXPORT_IMPL(Service::HID::Module)
namespace Service::HID { namespace Service::HID {
std::array<float, 4> Module::stylusInput = {0}; std::array<float, 4> Module::stylusInput = {};
std::array<float, 11> Module::modButtons = {0}; std::array<float, 4> Module::modButtons = {};
template <class Archive> template <class Archive>
void Module::serialize(Archive& ar, const unsigned int file_version) { void Module::serialize(Archive& ar, const unsigned int file_version) {
@ -231,12 +231,11 @@ void Module::UpdatePadCallback(std::uintptr_t user_data, s64 cycles_late) {
std::tie(c_stick_x_f, c_stick_y_f) = c_stick->GetStatus(); std::tie(c_stick_x_f, c_stick_y_f) = c_stick->GetStatus();
stylusInput[0] = c_stick_x_f; stylusInput[0] = c_stick_x_f;
stylusInput[1] = c_stick_y_f; stylusInput[1] = c_stick_y_f;
stylusInput[2] = zl_button->GetStatus(); stylusInput[2] = static_cast<float>(zl_button->GetStatus());
stylusInput[3] = zr_button->GetStatus(); stylusInput[3] = static_cast<float>(zr_button->GetStatus());
for (int i = 0; i < 12; i++){ for (int i = 0; i < 4; i++){
modButtons[i] = buttons[i - BUTTON_HID_BEGIN]->GetStatus(); modButtons[i] = zl_button->GetStatus() && buttons[i - BUTTON_HID_BEGIN]->GetStatus();
} }
// Get current circle pad position and update circle pad direction // Get current circle pad position and update circle pad direction
float circle_pad_x_f, circle_pad_y_f; float circle_pad_x_f, circle_pad_y_f;
std::tie(circle_pad_x_f, circle_pad_y_f) = circle_pad->GetStatus(); std::tie(circle_pad_x_f, circle_pad_y_f) = circle_pad->GetStatus();
@ -344,7 +343,7 @@ std::array<float, 4> Module::getStylusInputs(){
return stylusInput; return stylusInput;
} }
std::array<float, 11> Module::getModButtons(){ std::array<float, 4> Module::getModButtons(){
return modButtons; return modButtons;
} }

View file

@ -336,7 +336,7 @@ public:
void ReloadInputDevices(); void ReloadInputDevices();
static std::array<float, 4> getStylusInputs(); static std::array<float, 4> getStylusInputs();
static std::array<float, 11> getModButtons(); static std::array<float, 4> getModButtons();
const PadState& GetState() const; const PadState& GetState() const;
// Updating period for each HID device. These empirical values are measured from a 11.2 3DS. // Updating period for each HID device. These empirical values are measured from a 11.2 3DS.
@ -398,7 +398,7 @@ private:
std::unique_ptr<Input::TouchDevice> touch_device; std::unique_ptr<Input::TouchDevice> touch_device;
std::unique_ptr<Input::TouchDevice> touch_btn_device; std::unique_ptr<Input::TouchDevice> touch_btn_device;
static std::array<float, 4> stylusInput; static std::array<float, 4> stylusInput;
static std::array<float, 11> modButtons; static std::array<float, 4> modButtons;
std::shared_ptr<ArticBaseController> artic_controller; std::shared_ptr<ArticBaseController> artic_controller;
std::shared_ptr<Network::ArticBase::Client> artic_client; std::shared_ptr<Network::ArticBase::Client> artic_client;

View file

@ -17,7 +17,7 @@ uniform int layer;
in vec2 pixelUnit; in vec2 pixelUnit;
void main() { void main() {
vec4 pixel = texture(color_texture, frag_tex_coord); vec4 pixel = texture(color_texture, frag_tex_coord);
vec2 rfrag_tex_coord = vec2(frag_tex_coord.y, 1.0 - frag_tex_coord.x); vec2 rfrag_tex_coord = vec2(frag_tex_coord.y, frag_tex_coord.x);
//Cursor //Cursor
if (cursor_enable){ if (cursor_enable){
//Black Outline //Black Outline

View file

@ -40,7 +40,7 @@ vec4 GetScreen(int screen_id) {
void main() { void main() {
vec4 pixel = GetScreen(screen_id_l); vec4 pixel = GetScreen(screen_id_l);
vec2 rfrag_tex_coord = vec2(frag_tex_coord.y, 1.0 - frag_tex_coord.x); vec2 rfrag_tex_coord = vec2(frag_tex_coord.y, frag_tex_coord.x);
//Cursor //Cursor
if (cursor_enable == 1){ if (cursor_enable == 1){
//Black Outline //Black Outline

View file

@ -7,6 +7,8 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "core/frontend/framebuffer_layout.h" #include "core/frontend/framebuffer_layout.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
#include "core\frontend\cursor.h"
class Cursor;
namespace Frontend { namespace Frontend {
class EmuWindow; class EmuWindow;
@ -109,6 +111,7 @@ protected:
RendererSettings settings; RendererSettings settings;
Frontend::EmuWindow& render_window; /// Reference to the render window handle. Frontend::EmuWindow& render_window; /// Reference to the render window handle.
Frontend::EmuWindow* secondary_window; /// Reference to the secondary render window handle. Frontend::EmuWindow* secondary_window; /// Reference to the secondary render window handle.
Cursor* vCursor;
protected: protected:
f32 current_fps = 0.0f; /// Current framerate, should be set by the renderer f32 current_fps = 0.0f; /// Current framerate, should be set by the renderer

View file

@ -78,6 +78,8 @@ RendererOpenGL::RendererOpenGL(Core::System& system, Pica::PicaCore& pica_,
rasterizer{system.Memory(), pica, system.CustomTexManager(), *this, driver}, rasterizer{system.Memory(), pica, system.CustomTexManager(), *this, driver},
frame_dumper{system, window} { frame_dumper{system, window} {
const bool has_debug_tool = driver.HasDebugTool(); const bool has_debug_tool = driver.HasDebugTool();
vCursor = new Cursor();
vCursor->setEmuWindow(&window);
window.mailbox = std::make_unique<OGLTextureMailbox>(has_debug_tool); window.mailbox = std::make_unique<OGLTextureMailbox>(has_debug_tool);
if (secondary_window) { if (secondary_window) {
secondary_window->mailbox = std::make_unique<OGLTextureMailbox>(has_debug_tool); secondary_window->mailbox = std::make_unique<OGLTextureMailbox>(has_debug_tool);
@ -235,6 +237,7 @@ void RendererOpenGL::RenderToMailbox(const Layout::FramebufferLayout& layout,
state.draw.draw_framebuffer = frame->render.handle; state.draw.draw_framebuffer = frame->render.handle;
state.Apply(); state.Apply();
vCursor->update();
DrawScreens(layout, flipped); DrawScreens(layout, flipped);
// Create a fence for the frontend to wait on and swap this frame to OffTex // Create a fence for the frontend to wait on and swap this frame to OffTex
frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
@ -568,7 +571,7 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float x, fl
} else { } else {
glUniform1i(uniform_cursor_enable, 0); glUniform1i(uniform_cursor_enable, 0);
} }
glUniform2f(uniform_cursor_pos, cursor_pos[0]/320.0f, cursor_pos[1]/240.0f); glUniform2f(uniform_cursor_pos, vCursor->cursorPos[0]/320.0f, vCursor->cursorPos[1]/240.0f);
state.texture_units[0].texture_2d = screen_info.display_texture; state.texture_units[0].texture_2d = screen_info.display_texture;
state.texture_units[0].sampler = sampler; state.texture_units[0].sampler = sampler;
state.Apply(); state.Apply();
@ -645,7 +648,7 @@ void RendererOpenGL::DrawSingleScreenStereo(const ScreenInfo& screen_info_l,
} else { } else {
glUniform1i(uniform_cursor_enable, 0); glUniform1i(uniform_cursor_enable, 0);
} }
glUniform2f(uniform_cursor_pos, cursor_pos[0]/320.0f, cursor_pos[1]/240.0f); glUniform2f(uniform_cursor_pos, vCursor->cursorPos[0]/320.0f, vCursor->cursorPos[1]/240.0f);
state.texture_units[0].texture_2d = screen_info_l.display_texture; state.texture_units[0].texture_2d = screen_info_l.display_texture;
state.texture_units[1].texture_2d = screen_info_r.display_texture; state.texture_units[1].texture_2d = screen_info_r.display_texture;
state.texture_units[0].sampler = sampler; state.texture_units[0].sampler = sampler;
@ -706,8 +709,6 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f
} }
glUniform1i(uniform_layer, 0); glUniform1i(uniform_layer, 0);
cursor_pos[0] += 1;
cursor_pos[1] += 1;
if (!Settings::values.swap_screen.GetValue()) { if (!Settings::values.swap_screen.GetValue()) {
DrawTopScreen(layout, top_screen); DrawTopScreen(layout, top_screen);
glUniform1i(uniform_layer, 0); glUniform1i(uniform_layer, 0);

View file

@ -114,7 +114,6 @@ private:
GLuint attrib_position; GLuint attrib_position;
GLuint attrib_tex_coord; GLuint attrib_tex_coord;
int currScreenDraw; // 0 is Top, 1 is Bottom int currScreenDraw; // 0 is Top, 1 is Bottom
std::array<int, 2> cursor_pos = {0,0};
FrameDumperOpenGL frame_dumper; FrameDumperOpenGL frame_dumper;
}; };

View file

@ -127,6 +127,8 @@ RendererVulkan::RendererVulkan(Core::System& system, Pica::PicaCore& pica_,
update_queue, update_queue,
main_present_window.ImageCount()}, main_present_window.ImageCount()},
present_heap{instance, scheduler.GetMasterSemaphore(), PRESENT_BINDINGS, 32} { present_heap{instance, scheduler.GetMasterSemaphore(), PRESENT_BINDINGS, 32} {
vCursor = new Cursor();
vCursor->setEmuWindow(&window);
CompileShaders(); CompileShaders();
BuildLayouts(); BuildLayouts();
BuildPipelines(); BuildPipelines();
@ -249,7 +251,7 @@ void RendererVulkan::RenderToWindow(PresentWindow& window, const Layout::Framebu
clear_color.float32[1] = Settings::values.bg_green.GetValue(); clear_color.float32[1] = Settings::values.bg_green.GetValue();
clear_color.float32[2] = Settings::values.bg_blue.GetValue(); clear_color.float32[2] = Settings::values.bg_blue.GetValue();
clear_color.float32[3] = 1.0f; clear_color.float32[3] = 1.0f;
vCursor->update();
DrawScreens(frame, layout, flipped); DrawScreens(frame, layout, flipped);
scheduler.Flush(frame->render_ready); scheduler.Flush(frame->render_ready);
@ -793,7 +795,7 @@ void RendererVulkan::DrawSingleScreen(u32 screen_id, float x, float y, float w,
} else { } else {
draw_info.cursor_enable = 0; draw_info.cursor_enable = 0;
} }
draw_info.cursor_pos = Common::MakeVec(cursor_pos[0]/320.0f, cursor_pos[1]/240.0f); draw_info.cursor_pos = Common::MakeVec(vCursor->cursorPos[0]/320.0f, vCursor->cursorPos[1]/240.0f);
draw_info.screen_id_l = screen_id; draw_info.screen_id_l = screen_id;
scheduler.Record([this, offset = offset, info = draw_info](vk::CommandBuffer cmdbuf) { scheduler.Record([this, offset = offset, info = draw_info](vk::CommandBuffer cmdbuf) {
@ -871,7 +873,7 @@ void RendererVulkan::DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, fl
} else { } else {
draw_info.cursor_enable = 0; draw_info.cursor_enable = 0;
} }
draw_info.cursor_pos = Common::MakeVec(cursor_pos[0]/320.0f, cursor_pos[1]/240.0f); draw_info.cursor_pos = Common::MakeVec(vCursor->cursorPos[0]/320.0f, vCursor->cursorPos[1]/240.0f);
draw_info.screen_id_l = screen_id_l; draw_info.screen_id_l = screen_id_l;
draw_info.screen_id_r = screen_id_r; draw_info.screen_id_r = screen_id_r;
@ -1020,8 +1022,6 @@ void RendererVulkan::DrawScreens(Frame* frame, const Layout::FramebufferLayout&
if (settings.shader_update_requested.exchange(false)) { if (settings.shader_update_requested.exchange(false)) {
ReloadPipeline(layout.render_3d_mode); ReloadPipeline(layout.render_3d_mode);
} }
cursor_pos[0] += 1;
cursor_pos[1] += 1;
PrepareDraw(frame, layout); PrepareDraw(frame, layout);
const auto& top_screen = layout.top_screen; const auto& top_screen = layout.top_screen;

View file

@ -6,6 +6,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/math_util.h" #include "common/math_util.h"
#include "core/frontend/cursor.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#ifdef HAVE_LIBRETRO #ifdef HAVE_LIBRETRO
#include "citra_libretro/libretro_vk.h" #include "citra_libretro/libretro_vk.h"
@ -154,7 +155,6 @@ private:
vk::Pipeline cursor_pipeline{}; vk::Pipeline cursor_pipeline{};
vk::UniquePipelineLayout cursor_pipeline_layout{}; vk::UniquePipelineLayout cursor_pipeline_layout{};
int currScreenDraw; // 0 is Top, 1 is Bottom int currScreenDraw; // 0 is Top, 1 is Bottom
std::array<int, 2> cursor_pos = {0,0};
}; };
} // namespace Vulkan } // namespace Vulkan