setup cursor class and related variables/methods

This commit is contained in:
KojoZero 2026-04-26 14:18:14 -07:00
parent 93cdb65693
commit bb5dbf4c0f
7 changed files with 349 additions and 4 deletions

View file

@ -121,6 +121,8 @@ add_library(citra_core STATIC
frontend/camera/interface.h
frontend/emu_window.cpp
frontend/emu_window.h
frontend/cursor.cpp
frontend/cursor.h
frontend/framebuffer_layout.cpp
frontend/framebuffer_layout.h
frontend/image_interface.cpp

View file

@ -0,0 +1,270 @@
#include "cursor.h"
#include <cmath>
#include <algorithm>
#include "common/logging/log.h"
#include "core\hle\service\hid\hid.h"
void Cursor::update(){
if (emuWindow != nullptr){
stylusInput = Service::HID::Module::getStylusInputs();
modButtons = Service::HID::Module::getModButtons();
if (deviceInUse == 0){
if (inMacro){
runMacro();
} else {
// Reset the cursor position if macro was just played
if (justFinishedMacro > 0){
justFinishedMacro = 0;
rawCursorPos[0] = macroInitPos[0];
rawCursorPos[1] = macroInitPos[1];
}
// Macros
if (modButtons[0]){
circle(0);
return;
} else if (modButtons[1]){
rub();
return;
} else if (modButtons[2]){
if (!macroBtnPressed){
// Add macro
return;
}
} else if (modButtons[3]){
circle(1);
return;
} else {
if (macroBtnPressed){
macroBtnPressed = false;
}
}
normStylusDirection[0] = stylusInput[0];
normStylusDirection[1] = stylusInput[1];
int maxSpeed = 50;
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;
bool stylusModPressed = stylusInput[3];
float responsecurve = 175.0f / 100.0f;
float speedupratio = 400.0f / 100.0f;
float joystickScaled[2] = {0.0f};
float radialLength = std::sqrt((normStylusDirection[0] * normStylusDirection[0]) + (normStylusDirection[1] * normStylusDirection[1]));
float finalLength;
float curvedLength;
if (radialLength > 0) {
// Get X and Y as a relation to the radial length
float rComponents[2];
rComponents[0] = normStylusDirection[0]/radialLength;
rComponents[1] = normStylusDirection[1]/radialLength;
// Apply response curve and output
curvedLength = std::pow(radialLength, responsecurve);
finalLength = stylusModPressed ? curvedLength * speedupratio : curvedLength;
joystickScaled[0] = rComponents[0] * finalLength;
joystickScaled[1] = rComponents[1] * finalLength;
}
// The code below sets the cursor position to the position of the joystick (absolute). Needs to be readjusted for standalone melonDS
// _joystickCursorPosition = vec2((NDS_SCREEN_WIDTH/2.0f)+(std::min<float>(1.0,(normStylusDirection[0]/0.7071))*(NDS_SCREEN_WIDTH/2.0f)), (NDS_SCREEN_HEIGHT/2.0f)+(std::min<float>(1.0,(normStylusDirection[1]/0.7071))*(NDS_SCREEN_HEIGHT/2.0f)));
float tempX = joystickScaled[0];
float tempY = joystickScaled[1];
switch (rotation)
{
case 1: // 90°
joystickScaled[0] = tempY;
joystickScaled[1] = -tempX;
break;
case 2: // 180°
joystickScaled[0] = -tempX;
joystickScaled[1] = -tempY;
break;
case 3: // 270°
joystickScaled[0] = -tempY;
joystickScaled[1] = tempX;
break;
default:
break;
}
rawCursorPos[0] += joystickScaled[0]*heightSpeed;
rawCursorPos[1] += joystickScaled[1]*heightSpeed;
// Clamp to region and ready position information for touchscreen
clamp();
updateCursorPos();
// Handle stylus touch button presses
if (stylusInput[5]){
touchScreen();
wasTouching = true;
} else if (wasTouching && !stylusInput[5]){
release();
wasTouching = false;
}
}
} else {
//Update cursor based on mouse position
clamp();
updateCursorPos();
// Handle stylus touch button presses
if (stylusInput[5]){
touchScreen();
wasTouching = true;
} else if (wasTouching && !stylusInput[5]){
release();
wasTouching = false;
}
setDeviceInUse(0);
}
}
}
void Cursor::setDeviceInUse(int device){
deviceInUse = device;
}
void Cursor::setRawCursorPos(float x, float y){
rawCursorPos[0] = x;
rawCursorPos[1] = y;
}
void Cursor::clamp(){
rawCursorPos[0] = std::clamp(rawCursorPos[0], 0.0f, 319.0f);
rawCursorPos[1] = std::clamp(rawCursorPos[1], 0.0f, 239.0f);
}
void Cursor::touchScreen(){
emuWindow->TouchDirectlyPressed(cursorPos[0], cursorPos[1]);
}
void Cursor::release(){
emuWindow->TouchReleased();
}
void Cursor::setEmuWindow(Frontend::EmuWindow* emuWindow){
this->emuWindow = emuWindow;
}
void Cursor::updateCursorPos(){
cursorPos[0] = std::floor(rawCursorPos[0]);
cursorPos[1] = std::floor(rawCursorPos[1]);
}
void Cursor::circle(int direction){
macroBtnPressed = true;
inMacro = true;
wasTouching = true;
macroType = 1;
float radius = 240.0f/4.0f;
if (justFinishedMacro != 1){ // Set the original position if just starting
macroInitPos = {rawCursorPos[0], rawCursorPos[1]};
}
std::vector<std::array<float, 2>> offsetArray;
if (direction == 0){
offsetArray.push_back({(0.0f*radius), (-1.0f*radius)});
offsetArray.push_back({(0.7071f*radius), (-0.7071f*radius)});
offsetArray.push_back({(1.0f*radius), (0.0f*radius)});
offsetArray.push_back({(0.7071f*radius), (0.7071f*radius)});
offsetArray.push_back({(0.0f*radius), (1.0f*radius)});
offsetArray.push_back({(-0.7071f*radius), (0.7071f*radius)});
offsetArray.push_back({(-1.0f*radius), (0.0f*radius)});
offsetArray.push_back({(-0.7071f*radius), (-0.7071f*radius)});
} else {
offsetArray.push_back({(0.0f*radius), (-1.0f*radius)});
offsetArray.push_back({(-0.7071f*radius), (-0.7071f*radius)});
offsetArray.push_back({(-1.0f*radius), (0.0f*radius)});
offsetArray.push_back({(-0.7071f*radius), (0.7071f*radius)});
offsetArray.push_back({(0.0f*radius), (1.0f*radius)});
offsetArray.push_back({(0.7071f*radius), (0.7071f*radius)});
offsetArray.push_back({(1.0f*radius), (0.0f*radius)});
offsetArray.push_back({(0.7071f*radius), (-0.7071f*radius)});
}
offsetArray = rotateVector(offsetArray);
for (int i = 0; i < offsetArray.size(); i++){
macroPositions.push_back({rawCursorPos[0]+offsetArray[i][0], rawCursorPos[1]+offsetArray[i][1]});
}
macroFrames = macroPositions.size();
runMacro();
}
void Cursor::rub(){
macroBtnPressed = true;
inMacro = true;
wasTouching = true;
macroType = 2;
float radius = 240.0f/6.0f;
if (justFinishedMacro != 2){ // Set the original position if just starting
macroInitPos = {rawCursorPos[0], rawCursorPos[1]};
}
std::vector<std::array<float, 2>> offsetArray;
offsetArray.push_back({(0.0f*radius), 0});
offsetArray.push_back({(0.5f*radius), 0});
offsetArray.push_back({(1.0f*radius), 0});
offsetArray.push_back({(0.5f*radius), 0});
offsetArray.push_back({(0.0f*radius), 0});
offsetArray.push_back({(-0.5f*radius), 0});
offsetArray.push_back({(-1.0f*radius), 0});
offsetArray.push_back({(-0.5f*radius), 0});
offsetArray = rotateVector(offsetArray);
for (int i = 0; i < offsetArray.size(); i++){
macroPositions.push_back({rawCursorPos[0]+offsetArray[i][0], rawCursorPos[1]+offsetArray[i][1]});
}
macroFrames = macroPositions.size();
runMacro();
}
void Cursor::runMacro(){
rawCursorPos[0] = macroPositions.front()[0];
rawCursorPos[1] = macroPositions.front()[1];
macroPositions.pop_front();
clamp();
updateCursorPos();
touchScreen();
macroFrames--;
if (macroFrames == 0){
macroPositions.clear();
inMacro = false;
justFinishedMacro = macroType;
}
}
void Cursor::setRotation(int rot){
rotation = rot;
}
void Cursor::setLayout(int lay){
layout = lay;
}
std::vector<std::array<float, 2>> Cursor::rotateVector(std::vector<std::array<float, 2>> input){
for (auto& currArray : input){
float tempX = currArray[0];
float tempY = currArray[1];
switch (rotation)
{
case 1: // 90°
currArray[0] = tempY;
currArray[1] = -tempX;
break;
case 2: // 180°
currArray[0] = -tempX;
currArray[1] = -tempY;
break;
case 3: // 270°
currArray[0] = -tempY;
currArray[1] = tempX;
break;
default:
break;
}
}
return input;
}

View file

@ -0,0 +1,50 @@
#ifndef CURSOR_H
#define CURSOR_H
#include <queue>
#include "emu_window.h"
#include <array>
#include <vector>
namespace Frontend {
class EmuWindow;
}
class Cursor
{
public:
void update();
void setRawCursorPos(float x, float y);
void setDeviceInUse(int device);
void setEmuWindow(Frontend::EmuWindow* emuWindow);
void setRotation(int rot);
void setLayout(int layout);
int cursorPos[2];
float normStylusDirection[2];
bool cursorEnabled = true;
private:
Frontend::EmuWindow* emuWindow = nullptr;
float rawCursorPos[2] = {159, 119};
void touchScreen();
void release();
void clamp();
void updateCursorPos();
bool wasTouching;
std::array<float, 4> stylusInput;
std::array<float, 11> modButtons;
void circle(int direction); //0 is clockwise, 1 is counter clockwise
void rub();
void runMacro();
std::vector<std::array<float, 2>> rotateVector(std::vector<std::array<float, 2>> input);
bool inMacro;
std::deque<std::array<float, 2>> macroPositions;
int macroFrames;
bool macroBtnPressed;
int macroType;
int justFinishedMacro;
std::array<float, 2> macroInitPos;
int rotation;
int layout;
int deviceInUse; //0 is Gamepad, 1 is Mouse/Tablet
};
#endif // CURSOR_H

View file

@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cmath>
#include <mutex>
#include "common/settings.h"
@ -191,6 +192,15 @@ bool EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
return true;
}
bool EmuWindow::TouchDirectlyPressed(unsigned internal_x, unsigned internal_y) {
std::scoped_lock guard{touch_state->mutex};
std::clamp<unsigned>(internal_x, 0, 319);
std::clamp<unsigned>(internal_y, 0, 239);
touch_state->touch_pressed = true;
touch_state->touch_x = internal_x;
touch_state->touch_y = internal_y;
return true;
}
void EmuWindow::TouchReleased() {
std::scoped_lock guard{touch_state->mutex};
touch_state->touch_pressed = false;

View file

@ -199,6 +199,9 @@ public:
*/
bool TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y);
/// Signal a touch pressed event occured, bypassing the framebuffer (e.g. stylus touch button pressed on controller)
bool TouchDirectlyPressed(unsigned internal_x, unsigned internal_y);
/// Signal that a touch released event has occurred (e.g. mouse click released)
void TouchReleased();

View file

@ -24,11 +24,14 @@
#include "core/hle/service/ir/ir_user.h"
#include "core/hle/service/service.h"
#include "core/movie.h"
#include "hid.h"
SERVICE_CONSTRUCT_IMPL(Service::HID::Module)
SERIALIZE_EXPORT_IMPL(Service::HID::Module)
namespace Service::HID {
std::array<float, 4> Module::stylusInput = {0};
std::array<float, 11> Module::modButtons = {0};
template <class Archive>
void Module::serialize(Archive& ar, const unsigned int file_version) {
@ -223,7 +226,6 @@ void Module::UpdatePadCallback(std::uintptr_t user_data, s64 cycles_late) {
state.debug.Assign(buttons[Debug - BUTTON_HID_BEGIN]->GetStatus());
state.gpio14.Assign(buttons[Gpio14 - BUTTON_HID_BEGIN]->GetStatus());
// Setting up inputs for Cursor Class.
float c_stick_x_f, c_stick_y_f;
std::tie(c_stick_x_f, c_stick_y_f) = c_stick->GetStatus();
@ -231,7 +233,9 @@ void Module::UpdatePadCallback(std::uintptr_t user_data, s64 cycles_late) {
stylusInput[1] = c_stick_y_f;
stylusInput[2] = zl_button->GetStatus();
stylusInput[3] = zr_button->GetStatus();
// LOG_INFO(Service_HID, "C-Stick X: {}, C-Stick Y: {}, ZL: {}, ZR: {}", stylusInput[0], stylusInput[1], stylusInput[2], stylusInput[3]);
for (int i = 0; i < 12; i++){
modButtons[i] = buttons[i - BUTTON_HID_BEGIN]->GetStatus();
}
// Get current circle pad position and update circle pad direction
float circle_pad_x_f, circle_pad_y_f;
@ -340,6 +344,10 @@ std::array<float, 4> Module::getStylusInputs(){
return stylusInput;
}
std::array<float, 11> Module::getModButtons(){
return modButtons;
}
void Module::UpdateAccelerometerCallback(std::uintptr_t user_data, s64 cycles_late) {
SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer());

View file

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