diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 9091d734b..f038eccfa 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -33,6 +33,7 @@ const std::array QtConfig::default_butt Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N, Qt::Key_O, Qt::Key_P, Qt::Key_1, Qt::Key_2, Qt::Key_B, Qt::Key_V, + Qt::Key_U, }; const std::array, Settings::NativeAnalog::NumAnalogs> QtConfig::default_analogs{{ diff --git a/src/citra_qt/configuration/configure_input.cpp b/src/citra_qt/configuration/configure_input.cpp index 2cc319968..67b697dae 100644 --- a/src/citra_qt/configuration/configure_input.cpp +++ b/src/citra_qt/configuration/configure_input.cpp @@ -171,7 +171,7 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent) ui->buttonDpadUp, ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight, ui->buttonL, ui->buttonR, ui->buttonStart, ui->buttonSelect, ui->buttonDebug, ui->buttonGpio14, ui->buttonZL, ui->buttonZR, - ui->buttonHome, ui->buttonPower, + ui->buttonHome, ui->buttonPower, ui->buttonToggleCStick, }; analog_map_buttons = {{ diff --git a/src/citra_qt/configuration/configure_input.ui b/src/citra_qt/configuration/configure_input.ui index 67cc7688f..bcc6820a2 100644 --- a/src/citra_qt/configuration/configure_input.ui +++ b/src/citra_qt/configuration/configure_input.ui @@ -29,7 +29,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -68,6 +68,14 @@ true + + + 0 + -180 + 416 + 802 + + @@ -88,7 +96,7 @@ - ZR: + Stylus Touch / ZR: @@ -124,7 +132,7 @@ - ZL: + Stylus Mod / ZL: @@ -344,17 +352,17 @@ false - - + + - + - Start: + Circle Mod: - + @@ -380,24 +388,6 @@ - - - - - - Home: - - - - - - - - - - - - @@ -416,24 +406,6 @@ - - - - - - Circle Mod: - - - - - - - - - - - - @@ -452,6 +424,42 @@ + + + + + + Start: + + + + + + + + + + + + + + + + + + Home: + + + + + + + + + + + + @@ -470,6 +478,24 @@ + + + + + + Toggle C-Stick: + + + + + + + + + + + + @@ -572,7 +598,7 @@ - QLayout::SetDefaultConstraint + QLayout::SizeConstraint::SetDefaultConstraint @@ -580,7 +606,7 @@ Deadzone: 0 - Qt::AlignHCenter + Qt::AlignmentFlag::AlignHCenter @@ -589,7 +615,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -634,25 +660,25 @@ - QLayout::SetDefaultConstraint + QLayout::SizeConstraint::SetDefaultConstraint - Qt::LeftToRight + Qt::LayoutDirection::LeftToRight Diagonals - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter - Qt::Horizontal + Qt::Orientation::Horizontal @@ -697,7 +723,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -707,7 +733,7 @@ - C-Stick + Stylus / C-Stick false @@ -868,14 +894,14 @@ Diagonals - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter - Qt::Horizontal + Qt::Orientation::Horizontal @@ -884,7 +910,7 @@ - QLayout::SetDefaultConstraint + QLayout::SizeConstraint::SetDefaultConstraint @@ -894,7 +920,7 @@ Deadzone: 0 - Qt::AlignHCenter + Qt::AlignmentFlag::AlignHCenter @@ -903,7 +929,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -919,7 +945,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -957,7 +983,7 @@ - Qt::LeftToRight + Qt::LayoutDirection::LeftToRight Motion / Touch... @@ -985,7 +1011,7 @@ - Qt::LeftToRight + Qt::LayoutDirection::LeftToRight Auto Map @@ -1013,7 +1039,7 @@ - Qt::LeftToRight + Qt::LayoutDirection::LeftToRight Clear All @@ -1041,7 +1067,7 @@ - Qt::LeftToRight + Qt::LayoutDirection::LeftToRight Restore Defaults @@ -1094,6 +1120,7 @@ buttonCircleMod buttonDebug buttonGpio14 + buttonToggleCStick buttonMotionTouch buttonAutoMap buttonClearAll diff --git a/src/common/settings.h b/src/common/settings.h index 4196557d1..82bd18826 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -151,6 +151,8 @@ enum Values { Home, Power, + ToggleCStick, + NumButtons, }; @@ -185,6 +187,7 @@ static const std::array mapping = {{ "button_zr", "button_home", "button_power", + "button_togglecstick", }}; } // namespace NativeButton diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index dfcb20a27..dbe2fe6bf 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -121,6 +121,8 @@ add_library(citra_core STATIC EXCLUDE_FROM_ALL 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 diff --git a/src/core/frontend/cursor.cpp b/src/core/frontend/cursor.cpp new file mode 100644 index 000000000..e85497a32 --- /dev/null +++ b/src/core/frontend/cursor.cpp @@ -0,0 +1,276 @@ +#include "cursor.h" +#include +#include +#include "common/logging/log.h" +#include "common/logging/types.h" +#include "core/hle/service/hid/hid.h" + + +void Cursor::update(){ + if (emuWindow != nullptr){ + stylusInput = Service::HID::Module::getStylusInputs(); + modButtons = Service::HID::Module::getModButtons(); + setRotation(); + if (deviceInUse == 0){ + if (!Service::HID::Module::cstickEnabled){ + if (inMacro){ + runMacro(); + } 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 + 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[2]; + 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(1.0,(normStylusDirection[0]/0.7071))*(NDS_SCREEN_WIDTH/2.0f)), (NDS_SCREEN_HEIGHT/2.0f)+(std::min(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[3]){ + touchScreen(); + wasTouching = true; + } else if (wasTouching && !stylusInput[3]){ + release(); + wasTouching = false; + } + } + } + } else { + //Update cursor based on mouse position + clamp(); + updateCursorPos(); + + // Handle stylus touch button presses + if (stylusInput[3]){ + touchScreen(); + wasTouching = true; + } else if (wasTouching && !stylusInput[3]){ + 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> 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 (std::size_t 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> 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 (std::size_t 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(){ + if (emuWindow->GetFramebufferLayout().is_portrait){ + rotation = 1; + } else { + rotation = 0; + } +} + +std::vector> Cursor::rotateVector(std::vector> 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; +} diff --git a/src/core/frontend/cursor.h b/src/core/frontend/cursor.h new file mode 100644 index 000000000..6147944e3 --- /dev/null +++ b/src/core/frontend/cursor.h @@ -0,0 +1,48 @@ +#ifndef CURSOR_H +#define CURSOR_H +#include +#include "emu_window.h" +#include +#include +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 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 stylusInput; + std::array modButtons; + void circle(int direction); //0 is clockwise, 1 is counter clockwise + void rub(); + void runMacro(); + + std::vector> rotateVector(std::vector> input); + bool inMacro; + std::deque> macroPositions; + int macroFrames; + bool macroBtnPressed; + int macroType; + int justFinishedMacro; + std::array macroInitPos; + int rotation; + int deviceInUse; //0 is Gamepad, 1 is Mouse/Tablet +}; + +#endif // CURSOR_H \ No newline at end of file diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 3216a1675..e56fe7ba4 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include #include "common/settings.h" @@ -191,6 +192,16 @@ bool EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) { return true; } +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::clamp(internal_x, 0, 319); + std::clamp(internal_y, 0, 239); + touch_state->touch_pressed = true; + touch_state->touch_x = internal_x/320.0f; + touch_state->touch_y = (239-internal_y)/240.0f; + return true; +} void EmuWindow::TouchReleased() { std::scoped_lock guard{touch_state->mutex}; touch_state->touch_pressed = false; diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 175f41860..cd2f81b95 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -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(); diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index 918c1454f..c0eb2fcb8 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -639,6 +639,7 @@ FramebufferLayout reverseLayout(FramebufferLayout layout) { layout.additional_screen.top = layout.height - oldRight; layout.additional_screen.bottom = layout.height - oldLeft; } + layout.is_portrait = true; return layout; } diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 22fbec8cb..77b51c922 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -24,12 +24,15 @@ #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 Module::stylusInput = {}; +std::array Module::modButtons = {}; +bool Module::cstickEnabled = false; template void Module::serialize(Archive& ar, const unsigned int file_version) { DEBUG_SERIALIZATION_POINT; @@ -121,9 +124,17 @@ void Module::LoadInputDevices() { Settings::values.current_input_profile.buttons.begin() + Settings::NativeButton::BUTTON_HID_END, buttons.begin(), Input::CreateDevice); + zl_button = Input::CreateDevice( + Settings::values.current_input_profile.buttons[Settings::NativeButton::ZL]); + zr_button = Input::CreateDevice( + Settings::values.current_input_profile.buttons[Settings::NativeButton::ZR]); + toggle_cstick_button = Input::CreateDevice( + Settings::values.current_input_profile.buttons[Settings::NativeButton::ToggleCStick]); circle_pad = Input::CreateDevice( Settings::values.current_input_profile.analogs[Settings::NativeAnalog::CirclePad]); - motion_device = Input::CreateDevice( + c_stick = Input::CreateDevice( + Settings::values.current_input_profile.analogs[Settings::NativeAnalog::CStick]); + motion_device = Input::CreateDevice( Settings::values.current_input_profile.motion_device); touch_device = Input::CreateDevice( Settings::values.current_input_profile.touch_device); @@ -217,6 +228,24 @@ 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(); + stylusInput[0] = c_stick_x_f; + stylusInput[1] = c_stick_y_f; + stylusInput[2] = static_cast(zl_button->GetStatus()); + stylusInput[3] = static_cast(zr_button->GetStatus()); + for (int i = 0; i < 4; i++){ + modButtons[i] = zl_button->GetStatus() && buttons[i - BUTTON_HID_BEGIN]->GetStatus(); + } + + //Toggle for Cstick + if (!prev_toggle_cstick_button_state && toggle_cstick_button->GetStatus()){ + cstickEnabled = !cstickEnabled; + } + //LOG_INFO(Service_HID, "C-Stick Enabled: {}, Prev C-Stick Button State {}, Current C-Stick Button State {}", cstickEnabled, prev_toggle_cstick_button_state, toggle_cstick_button->GetStatus()); + prev_toggle_cstick_button_state = toggle_cstick_button->GetStatus(); + // Get current circle pad position and update circle pad direction float circle_pad_x_f, circle_pad_y_f; std::tie(circle_pad_x_f, circle_pad_y_f) = circle_pad->GetStatus(); @@ -320,6 +349,14 @@ void Module::UpdatePadCallback(std::uintptr_t user_data, s64 cycles_late) { system.CoreTiming().ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); } +std::array Module::getStylusInputs(){ + return stylusInput; +} + +std::array Module::getModButtons(){ + return modButtons; +} + void Module::UpdateAccelerometerCallback(std::uintptr_t user_data, s64 cycles_late) { SharedMem* mem = reinterpret_cast(shared_mem->GetPointer()); diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 33250e18b..5fcf82642 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -242,9 +242,8 @@ public: class Interface : public ServiceFramework { public: Interface(std::shared_ptr hid, const char* name, u32 max_session); - + // Stylus X, Y, ZL, ZR std::shared_ptr GetModule() const; - protected: /** * HID::GetIPCHandles service function @@ -335,7 +334,9 @@ public: void UseArticClient(const std::shared_ptr& client); void ReloadInputDevices(); - + static std::array getStylusInputs(); + static std::array getModButtons(); + static bool cstickEnabled; const PadState& GetState() const; // Updating period for each HID device. These empirical values are measured from a 11.2 3DS. @@ -388,12 +389,18 @@ private: std::atomic is_device_reload_pending{true}; std::array, Settings::NativeButton::NUM_BUTTONS_HID> buttons; + std::unique_ptr zl_button; + std::unique_ptr zr_button; + std::unique_ptr toggle_cstick_button; std::unique_ptr circle_pad; + std::unique_ptr c_stick; std::unique_ptr motion_device; std::unique_ptr controller_touch_device; std::unique_ptr touch_device; std::unique_ptr touch_btn_device; - + static std::array stylusInput; + static std::array modButtons; + bool prev_toggle_cstick_button_state = false; std::shared_ptr artic_controller; std::shared_ptr artic_client; diff --git a/src/core/hle/service/ir/extra_hid.cpp b/src/core/hle/service/ir/extra_hid.cpp index e5dc486cb..fcb1b7dab 100644 --- a/src/core/hle/service/ir/extra_hid.cpp +++ b/src/core/hle/service/ir/extra_hid.cpp @@ -262,13 +262,20 @@ void ExtraHID::SendHIDStatus() { } else { float x, y; std::tie(x, y) = c_stick->GetStatus(); - - response.c_stick.header.Assign(static_cast(ResponseID::PollHID)); - response.c_stick.c_stick_x.Assign(static_cast(C_STICK_CENTER + C_STICK_RADIUS * x)); - response.c_stick.c_stick_y.Assign(static_cast(C_STICK_CENTER + C_STICK_RADIUS * y)); response.buttons.battery_level.Assign(0x1F); - response.buttons.zl_not_held.Assign(!zl->GetStatus()); - response.buttons.zr_not_held.Assign(!zr->GetStatus()); + response.c_stick.header.Assign(static_cast(ResponseID::PollHID)); + if (Service::HID::Module::cstickEnabled){ + response.c_stick.c_stick_x.Assign(static_cast(C_STICK_CENTER + C_STICK_RADIUS * x)); + response.c_stick.c_stick_y.Assign(static_cast(C_STICK_CENTER + C_STICK_RADIUS * y)); + response.buttons.zl_not_held.Assign(!zl->GetStatus()); + response.buttons.zr_not_held.Assign(!zr->GetStatus()); + } else { + response.c_stick.c_stick_x.Assign(static_cast(C_STICK_CENTER)); + response.c_stick.c_stick_y.Assign(static_cast(C_STICK_CENTER)); + response.buttons.zl_not_held.Assign(true); + response.buttons.zr_not_held.Assign(true); + } + response.buttons.r_not_held.Assign(1); response.unknown = 0; } diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 71f2be379..7a22d4e4c 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -140,6 +140,7 @@ constexpr std::array= (cursor_pos.x - (1.0*pixelUnit.x))) { + if (rfrag_tex_coord.y <= (cursor_pos.y + (5.0*pixelUnit.y)) && + rfrag_tex_coord.y >= (cursor_pos.y - (4.0*pixelUnit.y))) { + pixel = vec4(0.0, 0.0, 0.0, 1.0); + } + } + + if (rfrag_tex_coord.y <= (cursor_pos.y + (2.0*pixelUnit.y)) && + rfrag_tex_coord.y >= (cursor_pos.y - (1.0*pixelUnit.y))) { + if (rfrag_tex_coord.x <= (cursor_pos.x + (5.0*pixelUnit.x)) && + rfrag_tex_coord.x >= (cursor_pos.x - (4.0*pixelUnit.x))) { + pixel = vec4(0.0, 0.0, 0.0, 1.0); + } + } + //White Cross + if (rfrag_tex_coord.x <= (cursor_pos.x + (1.0*pixelUnit.x)) && + rfrag_tex_coord.x >= (cursor_pos.x - (0.0*pixelUnit.x))) { + if (rfrag_tex_coord.y <= (cursor_pos.y + (4.0*pixelUnit.y)) && + rfrag_tex_coord.y >= (cursor_pos.y - (3.0*pixelUnit.y))) { + pixel = vec4(1.0, 1.0, 1.0, 1.0); + } + } + + if (rfrag_tex_coord.y <= (cursor_pos.y + (1.0*pixelUnit.y)) && + rfrag_tex_coord.y >= (cursor_pos.y - (0.0*pixelUnit.y))) { + if (rfrag_tex_coord.x <= (cursor_pos.x + (4.0*pixelUnit.x)) && + rfrag_tex_coord.x >= (cursor_pos.x - (3.0*pixelUnit.x))) { + pixel = vec4(1.0, 1.0, 1.0, 1.0); + } + } + } + + color = vec4(pixel.rgb, 1.0); } diff --git a/src/video_core/host_shaders/opengl_present.vert b/src/video_core/host_shaders/opengl_present.vert index f2a7a0b11..8e00a5be9 100644 --- a/src/video_core/host_shaders/opengl_present.vert +++ b/src/video_core/host_shaders/opengl_present.vert @@ -13,11 +13,13 @@ layout(location = 0) out vec2 frag_tex_coord; // The third row could be used for projection, which we don't need in 2D. It hence is assumed to // implicitly be [0, 0, 1] uniform mat3x2 modelview_matrix; - +out vec2 pixelUnit; void main() { // Multiply input position by the rotscale part of the matrix and then manually translate by // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector // to `vec3(vert_position.xy, 1.0)` gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0); frag_tex_coord = vert_tex_coord; + pixelUnit.x = 1/320.0; + pixelUnit.y = 1/240.0; } diff --git a/src/video_core/host_shaders/vulkan_present.frag b/src/video_core/host_shaders/vulkan_present.frag index 41c0916bd..2e412e5ce 100644 --- a/src/video_core/host_shaders/vulkan_present.frag +++ b/src/video_core/host_shaders/vulkan_present.frag @@ -7,11 +7,13 @@ layout (location = 0) in vec2 frag_tex_coord; layout (location = 0) out vec4 color; - +layout (location = 1) in vec2 pixelUnit; layout (push_constant, std140) uniform DrawInfo { mat4 modelview_matrix; vec4 i_resolution; vec4 o_resolution; + vec2 cursor_pos; + int cursor_enable; int screen_id_l; int screen_id_r; int layer; @@ -20,6 +22,7 @@ layout (push_constant, std140) uniform DrawInfo { layout (set = 0, binding = 0) uniform sampler2D screen_textures[3]; + vec4 GetScreen(int screen_id) { #ifdef ARRAY_DYNAMIC_INDEX return texture(screen_textures[screen_id], frag_tex_coord); @@ -36,5 +39,42 @@ vec4 GetScreen(int screen_id) { } void main() { - color = GetScreen(screen_id_l); + vec4 pixel = GetScreen(screen_id_l); + vec2 rfrag_tex_coord = vec2(frag_tex_coord.y, frag_tex_coord.x); + //Cursor + if (cursor_enable == 1){ + //Black Outline + if (rfrag_tex_coord.x <= (cursor_pos.x + (2.0*pixelUnit.x)) && + rfrag_tex_coord.x >= (cursor_pos.x - (1.0*pixelUnit.x))) { + if (rfrag_tex_coord.y <= (cursor_pos.y + (5.0*pixelUnit.y)) && + rfrag_tex_coord.y >= (cursor_pos.y - (4.0*pixelUnit.y))) { + pixel = vec4(0.0, 0.0, 0.0, 1.0); + } + } + + if (rfrag_tex_coord.y <= (cursor_pos.y + (2.0*pixelUnit.y)) && + rfrag_tex_coord.y >= (cursor_pos.y - (1.0*pixelUnit.y))) { + if (rfrag_tex_coord.x <= (cursor_pos.x + (5.0*pixelUnit.x)) && + rfrag_tex_coord.x >= (cursor_pos.x - (4.0*pixelUnit.x))) { + pixel = vec4(0.0, 0.0, 0.0, 1.0); + } + } + //White Cross + if (rfrag_tex_coord.x <= (cursor_pos.x + (1.0*pixelUnit.x)) && + rfrag_tex_coord.x >= (cursor_pos.x - (0.0*pixelUnit.x))) { + if (rfrag_tex_coord.y <= (cursor_pos.y + (4.0*pixelUnit.y)) && + rfrag_tex_coord.y >= (cursor_pos.y - (3.0*pixelUnit.y))) { + pixel = vec4(1.0, 1.0, 1.0, 1.0); + } + } + + if (rfrag_tex_coord.y <= (cursor_pos.y + (1.0*pixelUnit.y)) && + rfrag_tex_coord.y >= (cursor_pos.y - (0.0*pixelUnit.y))) { + if (rfrag_tex_coord.x <= (cursor_pos.x + (4.0*pixelUnit.x)) && + rfrag_tex_coord.x >= (cursor_pos.x - (3.0*pixelUnit.x))) { + pixel = vec4(1.0, 1.0, 1.0, 1.0); + } + } + } + color = vec4(pixel.rgb, 1.0); } diff --git a/src/video_core/host_shaders/vulkan_present.vert b/src/video_core/host_shaders/vulkan_present.vert index 08b4d252c..96ede7ec1 100644 --- a/src/video_core/host_shaders/vulkan_present.vert +++ b/src/video_core/host_shaders/vulkan_present.vert @@ -8,11 +8,13 @@ layout (location = 0) in vec2 vert_position; layout (location = 1) in vec2 vert_tex_coord; layout (location = 0) out vec2 frag_tex_coord; - +layout (location = 1) out vec2 pixelUnit; layout (push_constant, std140) uniform DrawInfo { mat4 modelview_matrix; vec4 i_resolution; vec4 o_resolution; + vec2 cursor_pos; + int cursor_enable; int screen_id_l; int screen_id_r; int layer; @@ -22,4 +24,6 @@ void main() { vec4 position = vec4(vert_position, 0.0, 1.0) * modelview_matrix; gl_Position = vec4(position.x, position.y, 0.0, 1.0); frag_tex_coord = vert_tex_coord; + pixelUnit.x = 1/320.0; + pixelUnit.y = 1/240.0; } diff --git a/src/video_core/host_shaders/vulkan_present_anaglyph.frag b/src/video_core/host_shaders/vulkan_present_anaglyph.frag index a01ce63f6..2d768d87a 100644 --- a/src/video_core/host_shaders/vulkan_present_anaglyph.frag +++ b/src/video_core/host_shaders/vulkan_present_anaglyph.frag @@ -24,6 +24,8 @@ layout (push_constant, std140) uniform DrawInfo { mat4 modelview_matrix; vec4 i_resolution; vec4 o_resolution; + vec2 cursor_pos; + int cursor_enable; int screen_id_l; int screen_id_r; int layer; diff --git a/src/video_core/host_shaders/vulkan_present_interlaced.frag b/src/video_core/host_shaders/vulkan_present_interlaced.frag index b033a9dec..b150413f1 100644 --- a/src/video_core/host_shaders/vulkan_present_interlaced.frag +++ b/src/video_core/host_shaders/vulkan_present_interlaced.frag @@ -12,6 +12,8 @@ layout (push_constant, std140) uniform DrawInfo { mat4 modelview_matrix; vec4 i_resolution; vec4 o_resolution; + vec2 cursor_pos; + int cursor_enable; int screen_id_l; int screen_id_r; int layer; diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 5a737a37f..47f18b4cc 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -7,6 +7,8 @@ #include "common/common_types.h" #include "core/frontend/framebuffer_layout.h" #include "video_core/rasterizer_interface.h" +#include "core/frontend/cursor.h" +class Cursor; namespace Frontend { class EmuWindow; @@ -109,6 +111,7 @@ protected: RendererSettings settings; Frontend::EmuWindow& render_window; /// Reference to the render window handle. Frontend::EmuWindow* secondary_window; /// Reference to the secondary render window handle. + Cursor* vCursor; protected: f32 current_fps = 0.0f; /// Current framerate, should be set by the renderer diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index c8cb6c000..324792f18 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -78,6 +78,8 @@ RendererOpenGL::RendererOpenGL(Core::System& system, Pica::PicaCore& pica_, rasterizer{system.Memory(), pica, system.CustomTexManager(), *this, driver}, frame_dumper{system, window} { const bool has_debug_tool = driver.HasDebugTool(); + vCursor = new Cursor(); + vCursor->setEmuWindow(&window); window.mailbox = std::make_unique(has_debug_tool); if (secondary_window) { secondary_window->mailbox = std::make_unique(has_debug_tool); @@ -235,6 +237,7 @@ void RendererOpenGL::RenderToMailbox(const Layout::FramebufferLayout& layout, state.draw.draw_framebuffer = frame->render.handle; state.Apply(); + vCursor->update(); DrawScreens(layout, flipped); // 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); @@ -433,6 +436,8 @@ void RendererOpenGL::ReloadShader(Settings::StereoRenderOption render_3d) { uniform_i_resolution = glGetUniformLocation(shader.handle, "i_resolution"); uniform_o_resolution = glGetUniformLocation(shader.handle, "o_resolution"); uniform_layer = glGetUniformLocation(shader.handle, "layer"); + uniform_cursor_pos = glGetUniformLocation(shader.handle, "cursor_pos"); + uniform_cursor_enable = glGetUniformLocation(shader.handle, "cursor_enable"); attrib_position = glGetAttribLocation(shader.handle, "vert_position"); attrib_tex_coord = glGetAttribLocation(shader.handle, "vert_tex_coord"); } @@ -556,11 +561,17 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float x, fl const u32 scale_factor = GetResolutionScaleFactor(); const GLuint sampler = samplers[Settings::values.filter_mode.GetValue()].handle; - glUniform4f(uniform_i_resolution, static_cast(screen_info.texture.width * scale_factor), - static_cast(screen_info.texture.height * scale_factor), - 1.0f / static_cast(screen_info.texture.width * scale_factor), - 1.0f / static_cast(screen_info.texture.height * scale_factor)); - glUniform4f(uniform_o_resolution, h, w, 1.0f / h, 1.0f / w); + glUniform4f(uniform_i_resolution, static_cast(screen_info.texture.height * scale_factor), + static_cast(screen_info.texture.width * scale_factor), + 1.0f / static_cast(screen_info.texture.height * scale_factor), + 1.0f / static_cast(screen_info.texture.width * scale_factor)); + glUniform4f(uniform_o_resolution, w, h, 1.0f / w, 1.0f / h); + if (currScreenDraw == 1){ + glUniform1i(uniform_cursor_enable, 1); + } else { + glUniform1i(uniform_cursor_enable, 0); + } + 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].sampler = sampler; state.Apply(); @@ -627,11 +638,17 @@ void RendererOpenGL::DrawSingleScreenStereo(const ScreenInfo& screen_info_l, const u32 scale_factor = GetResolutionScaleFactor(); const GLuint sampler = samplers[Settings::values.filter_mode.GetValue()].handle; glUniform4f(uniform_i_resolution, - static_cast(screen_info_l.texture.width * scale_factor), static_cast(screen_info_l.texture.height * scale_factor), - 1.0f / static_cast(screen_info_l.texture.width * scale_factor), - 1.0f / static_cast(screen_info_l.texture.height * scale_factor)); - glUniform4f(uniform_o_resolution, h, w, 1.0f / h, 1.0f / w); + static_cast(screen_info_l.texture.width * scale_factor), + 1.0f / static_cast(screen_info_l.texture.height * scale_factor), + 1.0f / static_cast(screen_info_l.texture.width * scale_factor)); + glUniform4f(uniform_o_resolution, w, h, 1.0f / w, 1.0f / h); + if (currScreenDraw == 1){ + glUniform1i(uniform_cursor_enable, 1); + } else { + glUniform1i(uniform_cursor_enable, 0); + } + 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[1].texture_2d = screen_info_r.display_texture; state.texture_units[0].sampler = sampler; @@ -736,6 +753,7 @@ void RendererOpenGL::DrawTopScreen(const Layout::FramebufferLayout& layout, if (!layout.top_screen_enabled) { return; } + currScreenDraw = 0; int leftside, rightside; leftside = Settings::values.swap_eyes_3d.GetValue() ? 1 : 0; rightside = Settings::values.swap_eyes_3d.GetValue() ? 0 : 1; @@ -797,7 +815,7 @@ void RendererOpenGL::DrawBottomScreen(const Layout::FramebufferLayout& layout, if (!layout.bottom_screen_enabled) { return; } - + currScreenDraw = 1; const float bottom_screen_left = static_cast(bottom_screen.left); const float bottom_screen_top = static_cast(bottom_screen.top); const float bottom_screen_width = static_cast(bottom_screen.GetWidth()); diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 2f2b318ed..68f0c260d 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -107,9 +107,13 @@ private: GLuint uniform_o_resolution; GLuint uniform_layer; + // Shader uniform for onscreen cursor + GLuint uniform_cursor_pos; + GLuint uniform_cursor_enable; // Shader attribute input indices GLuint attrib_position; GLuint attrib_tex_coord; + int currScreenDraw; // 0 is Top, 1 is Bottom FrameDumperOpenGL frame_dumper; }; diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 0a25c2036..85b1616cd 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -127,6 +127,8 @@ RendererVulkan::RendererVulkan(Core::System& system, Pica::PicaCore& pica_, update_queue, main_present_window.ImageCount()}, present_heap{instance, scheduler.GetMasterSemaphore(), PRESENT_BINDINGS, 32} { + vCursor = new Cursor(); + vCursor->setEmuWindow(&window); CompileShaders(); BuildLayouts(); 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[2] = Settings::values.bg_blue.GetValue(); clear_color.float32[3] = 1.0f; - + vCursor->update(); DrawScreens(frame, layout, flipped); scheduler.Flush(frame->render_ready); @@ -783,11 +785,17 @@ void RendererVulkan::DrawSingleScreen(u32 screen_id, float x, float y, float w, const u32 scale_factor = GetResolutionScaleFactor(); draw_info.i_resolution = - Common::MakeVec(static_cast(screen_info.texture.width * scale_factor), - static_cast(screen_info.texture.height * scale_factor), - 1.0f / static_cast(screen_info.texture.width * scale_factor), - 1.0f / static_cast(screen_info.texture.height * scale_factor)); - draw_info.o_resolution = Common::MakeVec(h, w, 1.0f / h, 1.0f / w); + Common::MakeVec(static_cast(screen_info.texture.height * scale_factor), + static_cast(screen_info.texture.width * scale_factor), + 1.0f / static_cast(screen_info.texture.height * scale_factor), + 1.0f / static_cast(screen_info.texture.width * scale_factor)); + draw_info.o_resolution = Common::MakeVec(w, h, 1.0f / w, 1.0f / h); + if (currScreenDraw == 1){ + draw_info.cursor_enable = 1; + } else { + draw_info.cursor_enable = 0; + } + draw_info.cursor_pos = Common::MakeVec(vCursor->cursorPos[0]/320.0f, vCursor->cursorPos[1]/240.0f); draw_info.screen_id_l = screen_id; scheduler.Record([this, offset = offset, info = draw_info](vk::CommandBuffer cmdbuf) { @@ -855,11 +863,17 @@ void RendererVulkan::DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, fl const u32 scale_factor = GetResolutionScaleFactor(); draw_info.i_resolution = - Common::MakeVec(static_cast(screen_info_l.texture.width * scale_factor), - static_cast(screen_info_l.texture.height * scale_factor), - 1.0f / static_cast(screen_info_l.texture.width * scale_factor), - 1.0f / static_cast(screen_info_l.texture.height * scale_factor)); - draw_info.o_resolution = Common::MakeVec(h, w, 1.0f / h, 1.0f / w); + Common::MakeVec(static_cast(screen_info_l.texture.height * scale_factor), + static_cast(screen_info_l.texture.width * scale_factor), + 1.0f / static_cast(screen_info_l.texture.height * scale_factor), + 1.0f / static_cast(screen_info_l.texture.width * scale_factor)); + draw_info.o_resolution = Common::MakeVec(w, h, 1.0f / w, 1.0f / h); + if (currScreenDraw == 1){ + draw_info.cursor_enable = 1; + } else { + draw_info.cursor_enable = 0; + } + 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_r = screen_id_r; @@ -886,6 +900,7 @@ void RendererVulkan::DrawTopScreen(const Layout::FramebufferLayout& layout, if (!layout.top_screen_enabled) { return; } + currScreenDraw = 0; int leftside, rightside; leftside = Settings::values.swap_eyes_3d.GetValue() ? 1 : 0; rightside = Settings::values.swap_eyes_3d.GetValue() ? 0 : 1; @@ -944,7 +959,7 @@ void RendererVulkan::DrawBottomScreen(const Layout::FramebufferLayout& layout, if (!layout.bottom_screen_enabled) { return; } - + currScreenDraw = 1; const float bottom_screen_left = static_cast(bottom_screen.left); const float bottom_screen_top = static_cast(bottom_screen.top); const float bottom_screen_width = static_cast(bottom_screen.GetWidth()); @@ -1007,7 +1022,6 @@ void RendererVulkan::DrawScreens(Frame* frame, const Layout::FramebufferLayout& if (settings.shader_update_requested.exchange(false)) { ReloadPipeline(layout.render_3d_mode); } - PrepareDraw(frame, layout); const auto& top_screen = layout.top_screen; diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 14c9bd34f..a5241df6b 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -6,6 +6,7 @@ #include "common/common_types.h" #include "common/math_util.h" +#include "core/frontend/cursor.h" #include "video_core/renderer_base.h" #ifdef HAVE_LIBRETRO #include "citra_libretro/libretro_vk.h" @@ -58,12 +59,14 @@ struct PresentUniformData { std::array modelview; Common::Vec4f i_resolution; Common::Vec4f o_resolution; + Common::Vec2f cursor_pos; + int cursor_enable = 0; int screen_id_l = 0; int screen_id_r = 0; int layer = 0; int reverse_interlaced = 0; }; -static_assert(sizeof(PresentUniformData) == 112, +static_assert(sizeof(PresentUniformData) == 124, "PresentUniformData does not structure in shader!"); class RendererVulkan : public VideoCore::RendererBase { @@ -151,6 +154,7 @@ private: vk::ShaderModule cursor_fragment_shader{}; vk::Pipeline cursor_pipeline{}; vk::UniquePipelineLayout cursor_pipeline_layout{}; + int currScreenDraw; // 0 is Top, 1 is Bottom }; } // namespace Vulkan