added c-stick toggle and stylus related gui changes

This commit is contained in:
KojoZero 2026-04-28 20:13:55 -07:00
parent ae3bd188c0
commit 0ba8e18dc4
7 changed files with 208 additions and 157 deletions

View file

@ -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 = {{

View file

@ -29,7 +29,7 @@
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -68,6 +68,14 @@
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaContents">
<property name="geometry">
<rect>
<x>0</x>
<y>-180</y>
<width>416</width>
<height>802</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QGridLayout" name="gridLayout_7">
@ -88,7 +96,7 @@
<item>
<widget class="QLabel" name="label_18">
<property name="text">
<string>ZR:</string>
<string>Stylus Touch / ZR:</string>
</property>
</widget>
</item>
@ -124,7 +132,7 @@
<item>
<widget class="QLabel" name="label_20">
<property name="text">
<string>ZL:</string>
<string>Stylus Mod / ZL:</string>
</property>
</widget>
</item>
@ -344,17 +352,17 @@
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_25">
<item row="2" column="0">
<layout class="QVBoxLayout" name="verticalLayout_28">
<item>
<widget class="QLabel" name="label_29">
<widget class="QLabel" name="label_36">
<property name="text">
<string>Start:</string>
<string>Circle Mod:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonStart">
<widget class="QPushButton" name="buttonCircleMod">
<property name="text">
<string/>
</property>
@ -380,24 +388,6 @@
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QVBoxLayout" name="verticalLayout_27">
<item>
<widget class="QLabel" name="label_31">
<property name="text">
<string>Home:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonHome">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<layout class="QVBoxLayout" name="verticalLayout_34">
<item>
@ -416,24 +406,6 @@
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QVBoxLayout" name="verticalLayout_28">
<item>
<widget class="QLabel" name="label_36">
<property name="text">
<string>Circle Mod:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonCircleMod">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<layout class="QVBoxLayout" name="verticalLayout_33">
<item>
@ -452,6 +424,42 @@
</item>
</layout>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_25">
<item>
<widget class="QLabel" name="label_29">
<property name="text">
<string>Start:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonStart">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QVBoxLayout" name="verticalLayout_27">
<item>
<widget class="QLabel" name="label_31">
<property name="text">
<string>Home:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonHome">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0">
<layout class="QVBoxLayout" name="verticalLayout_32">
<item>
@ -470,6 +478,24 @@
</item>
</layout>
</item>
<item row="3" column="1">
<layout class="QVBoxLayout" name="verticalLayout_40">
<item>
<widget class="QLabel" name="label_42">
<property name="text">
<string>Toggle C-Stick:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonToggleCStick">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
@ -572,7 +598,7 @@
<item>
<layout class="QHBoxLayout" name="sliderCirclePadDeadzoneAndModifierHorizontalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="labelCirclePadDeadzoneAndModifier">
@ -580,7 +606,7 @@
<string>Deadzone: 0</string>
</property>
<property name="alignment">
<set>Qt::AlignHCenter</set>
<set>Qt::AlignmentFlag::AlignHCenter</set>
</property>
</widget>
</item>
@ -589,7 +615,7 @@
<item>
<widget class="QSlider" name="sliderCirclePadDeadzoneAndModifier">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
@ -634,25 +660,25 @@
<item row="4" column="1" colspan="2">
<layout class="QVBoxLayout" name="verticalLayout_38">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="label_14">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
<enum>Qt::LayoutDirection::LeftToRight</enum>
</property>
<property name="text">
<string>Diagonals</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
@ -697,7 +723,7 @@
<item row="14" column="1" colspan="2">
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
@ -707,7 +733,7 @@
<item row="1" column="1">
<widget class="QGroupBox" name="faceButtons_5">
<property name="title">
<string>C-Stick</string>
<string>Stylus / C-Stick</string>
</property>
<property name="flat">
<bool>false</bool>
@ -868,14 +894,14 @@
<string>Diagonals</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
@ -884,7 +910,7 @@
<item row="9" column="0" colspan="2">
<layout class="QVBoxLayout" name="sliderCStickDeadzoneAndModifierVerticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<item>
<layout class="QHBoxLayout" name="sliderCStickDeadzoneAndModifierHorizontalLayout">
@ -894,7 +920,7 @@
<string>Deadzone: 0</string>
</property>
<property name="alignment">
<set>Qt::AlignHCenter</set>
<set>Qt::AlignmentFlag::AlignHCenter</set>
</property>
</widget>
</item>
@ -903,7 +929,7 @@
<item>
<widget class="QSlider" name="sliderCStickDeadzoneAndModifier">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
@ -919,7 +945,7 @@
<item row="7" column="0" colspan="2">
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
@ -957,7 +983,7 @@
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
<enum>Qt::LayoutDirection::LeftToRight</enum>
</property>
<property name="text">
<string>Motion / Touch...</string>
@ -985,7 +1011,7 @@
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
<enum>Qt::LayoutDirection::LeftToRight</enum>
</property>
<property name="text">
<string>Auto Map</string>
@ -1013,7 +1039,7 @@
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
<enum>Qt::LayoutDirection::LeftToRight</enum>
</property>
<property name="text">
<string>Clear All</string>
@ -1041,7 +1067,7 @@
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
<enum>Qt::LayoutDirection::LeftToRight</enum>
</property>
<property name="text">
<string>Restore Defaults</string>
@ -1094,6 +1120,7 @@
<tabstop>buttonCircleMod</tabstop>
<tabstop>buttonDebug</tabstop>
<tabstop>buttonGpio14</tabstop>
<tabstop>buttonToggleCStick</tabstop>
<tabstop>buttonMotionTouch</tabstop>
<tabstop>buttonAutoMap</tabstop>
<tabstop>buttonClearAll</tabstop>

View file

@ -151,6 +151,8 @@ enum Values {
Home,
Power,
ToggleCStick,
NumButtons,
};
@ -185,6 +187,7 @@ static const std::array<const char*, NumButtons> mapping = {{
"button_zr",
"button_home",
"button_power",
"button_togglecstick",
}};
} // namespace NativeButton

View file

@ -12,100 +12,102 @@ void Cursor::update(){
modButtons = Service::HID::Module::getModButtons();
setRotation();
if (deviceInUse == 0){
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;
if (!Service::HID::Module::cstickEnabled){
if (inMacro){
runMacro();
} else {
if (macroBtnPressed){
macroBtnPressed = false;
// 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];
}
}
normStylusDirection[0] = stylusInput[0];
normStylusDirection[1] = stylusInput[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;
}
}
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<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)));
normStylusDirection[0] = stylusInput[0];
normStylusDirection[1] = stylusInput[1];
float tempX = joystickScaled[0];
float tempY = joystickScaled[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<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)));
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;
}
float tempX = joystickScaled[0];
float tempY = joystickScaled[1];
rawCursorPos[0] += joystickScaled[0]*heightSpeed;
rawCursorPos[1] += joystickScaled[1]*heightSpeed;
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;
}
// Clamp to region and ready position information for touchscreen
clamp();
updateCursorPos();
rawCursorPos[0] += joystickScaled[0]*heightSpeed;
rawCursorPos[1] += joystickScaled[1]*heightSpeed;
// Handle stylus touch button presses
if (stylusInput[3]){
touchScreen();
wasTouching = true;
} else if (wasTouching && !stylusInput[3]){
release();
wasTouching = false;
// 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 {

View file

@ -32,7 +32,7 @@ SERIALIZE_EXPORT_IMPL(Service::HID::Module)
namespace Service::HID {
std::array<float, 4> Module::stylusInput = {};
std::array<float, 4> Module::modButtons = {};
bool Module::cstickEnabled = false;
template <class Archive>
void Module::serialize(Archive& ar, const unsigned int file_version) {
DEBUG_SERIALIZATION_POINT;
@ -128,6 +128,8 @@ void Module::LoadInputDevices() {
Settings::values.current_input_profile.buttons[Settings::NativeButton::ZL]);
zr_button = Input::CreateDevice<Input::ButtonDevice>(
Settings::values.current_input_profile.buttons[Settings::NativeButton::ZR]);
toggle_cstick_button = Input::CreateDevice<Input::ButtonDevice>(
Settings::values.current_input_profile.buttons[Settings::NativeButton::ToggleCStick]);
circle_pad = Input::CreateDevice<Input::AnalogDevice>(
Settings::values.current_input_profile.analogs[Settings::NativeAnalog::CirclePad]);
c_stick = Input::CreateDevice<Input::AnalogDevice>(
@ -236,6 +238,14 @@ void Module::UpdatePadCallback(std::uintptr_t user_data, s64 cycles_late) {
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();

View file

@ -244,7 +244,6 @@ public:
Interface(std::shared_ptr<Module> hid, const char* name, u32 max_session);
// Stylus X, Y, ZL, ZR
std::shared_ptr<Module> GetModule() const;
protected:
/**
* HID::GetIPCHandles service function
@ -337,6 +336,7 @@ public:
void ReloadInputDevices();
static std::array<float, 4> getStylusInputs();
static std::array<float, 4> getModButtons();
static bool cstickEnabled;
const PadState& GetState() const;
// Updating period for each HID device. These empirical values are measured from a 11.2 3DS.
@ -391,6 +391,7 @@ private:
buttons;
std::unique_ptr<Input::ButtonDevice> zl_button;
std::unique_ptr<Input::ButtonDevice> zr_button;
std::unique_ptr<Input::ButtonDevice> toggle_cstick_button;
std::unique_ptr<Input::AnalogDevice> circle_pad;
std::unique_ptr<Input::AnalogDevice> c_stick;
std::unique_ptr<Input::MotionDevice> motion_device;
@ -399,6 +400,7 @@ private:
std::unique_ptr<Input::TouchDevice> touch_btn_device;
static std::array<float, 4> stylusInput;
static std::array<float, 4> modButtons;
bool prev_toggle_cstick_button_state = false;
std::shared_ptr<ArticBaseController> artic_controller;
std::shared_ptr<Network::ArticBase::Client> artic_client;

View file

@ -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<u8>(ResponseID::PollHID));
response.c_stick.c_stick_x.Assign(static_cast<u32>(C_STICK_CENTER + C_STICK_RADIUS * x));
response.c_stick.c_stick_y.Assign(static_cast<u32>(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<u8>(ResponseID::PollHID));
if (Service::HID::Module::cstickEnabled){
response.c_stick.c_stick_x.Assign(static_cast<u32>(C_STICK_CENTER + C_STICK_RADIUS * x));
response.c_stick.c_stick_y.Assign(static_cast<u32>(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<u32>(C_STICK_CENTER));
response.c_stick.c_stick_y.Assign(static_cast<u32>(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;
}