From 93ff021d7e59126b4cbc378206b18edbbb17df28 Mon Sep 17 00:00:00 2001 From: NorthDroplet Date: Thu, 8 Oct 2026 08:04:48 +0000 Subject: [PATCH] Launcher: the trackpad pointer measures from where the stick rests, so it does not creep --- Launcher/Test/gamepad_input_tests.cpp | 24 ++++++++++++++++++++---- Launcher/gamepad_input.cpp | 25 +++++++++++++++++++++++-- Launcher/gamepad_input.h | 4 ++++ 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/Launcher/Test/gamepad_input_tests.cpp b/Launcher/Test/gamepad_input_tests.cpp index 2ce0c55..654980d 100644 --- a/Launcher/Test/gamepad_input_tests.cpp +++ b/Launcher/Test/gamepad_input_tests.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -432,13 +433,13 @@ int main() { PadFeed stick; PadFeed trackpad(true); ImGui::GetIO().AddMousePosEvent(400, 300); - frame(trackpad, pad()); + for (int i = 0; i < 20; ++i) frame(trackpad, pad()); // it settles first for (int i = 0; i < 10; ++i) frame(trackpad, pad(0, 3300, 0)); // a slow swipe frame(trackpad, pad()); check(ImGui::GetIO().MousePos.x > 405, "A slow swipe on the trackpad moves the pointer"); fresh(); ImGui::GetIO().AddMousePosEvent(400, 300); - frame(stick, pad()); + for (int i = 0; i < 20; ++i) frame(stick, pad()); for (int i = 0; i < 10; ++i) frame(stick, pad(0, 3300, 0)); frame(stick, pad()); check(!stick.pointing() && ImGui::GetIO().MousePos.x == 400, "The same small tilt of a thumbstick does not"); @@ -446,7 +447,7 @@ int main() { fresh(); PadFeed slowing(true); ImGui::GetIO().AddMousePosEvent(100, 300); - frame(slowing, pad()); + for (int i = 0; i < 20; ++i) frame(slowing, pad()); for (int i = 0; i < 5; ++i) frame(slowing, pad(0, 20000, 0)); // the swipe const float swiped = ImGui::GetIO().MousePos.x; float tilt = 20000; @@ -459,6 +460,21 @@ int main() { frame(slowing, pad(0, -900, -1200)); check(slowed > swiped + 60, "A swipe that slows down moves the pointer to its end"); check(ImGui::GetIO().MousePos.x == slowed, "The pointer stops when the thumb lifts"); + + fresh(); + PadFeed drifting(true); + ImGui::GetIO().AddMousePosEvent(400, 300); + for (int i = 0; i < 60; ++i) frame(drifting, pad(0, -2600, -2600)); // resting off the middle + check(!drifting.pointing() && ImGui::GetIO().MousePos.x == 400 && ImGui::GetIO().MousePos.y == 300, + "A trackpad resting off the middle does not move the pointer"); + for (int i = 0; i < 10; ++i) frame(drifting, pad(0, 9000, -2600)); + check(drifting.pointing() && ImGui::GetIO().MousePos.x > 420 && std::abs(ImGui::GetIO().MousePos.y - 300) < 1, + "A swipe moves it from where the trackpad rests"); + for (int i = 0; i < 60; ++i) frame(drifting, pad(0, -1000, 1500)); // lifted: resting somewhere new + const ImVec2 rested = ImGui::GetIO().MousePos; + for (int i = 0; i < 60; ++i) frame(drifting, pad(0, -1000, 1500)); + check(ImGui::GetIO().MousePos.x == rested.x && ImGui::GetIO().MousePos.y == rested.y, + "After a touch it rests again without the pointer creeping"); } // ------------------------------------------------ trackpad pointer @@ -468,7 +484,7 @@ int main() { auto& io = ImGui::GetIO(); io.AddMousePosEvent(400, 300); feed.mouse_moved(ImVec2(400, 300)); - frame(feed, pad()); + for (int i = 0; i < 20; ++i) frame(feed, pad()); frame(feed, pad(0, 3000, 0)); check(!feed.pointing() && io.MousePos.x == 400, "A thumb resting near the middle does not move the pointer"); diff --git a/Launcher/gamepad_input.cpp b/Launcher/gamepad_input.cpp index 3ee4169..7afd239 100644 --- a/Launcher/gamepad_input.cpp +++ b/Launcher/gamepad_input.cpp @@ -41,6 +41,12 @@ constexpr PointerFeel stick_feel{0.2f, 1400.0f, true}; // tilt drops back to rest the moment the thumb lifts. Small swipes must move // the pointer and a quick one must cross the window. constexpr PointerFeel trackpad_feel{0.06f, 2600.0f, false}; +// The right stick never rests quite at the middle, and Steam's trackpad rests +// a little off it too, somewhere new after each touch. A reading that holds +// this still, this near the middle, for this long is where it rests. +constexpr float rest_jitter = 0.01f; +constexpr float rest_limit = 0.12f; +constexpr float rest_seconds = 0.3f; // A mouse move this close to the last one is the same place reported again. constexpr float pointer_slack = 2.0f; @@ -105,9 +111,24 @@ void PadFeed::update(ImGuiIO& io, const PadState& input, float scale) { if (pressed & focus_bits) pointing_ = false; const PointerFeel& feel = trackpad_ ? trackpad_feel : stick_feel; - const float x = axis(pad.right_x), y = -axis(pad.right_y); // screen Y grows downward + const float raw_x = axis(pad.right_x), raw_y = -axis(pad.right_y); // screen Y grows downward + if (std::abs(raw_x - last_x_) < rest_jitter && std::abs(raw_y - last_y_) < rest_jitter && + std::sqrt(raw_x * raw_x + raw_y * raw_y) < rest_limit) { + steady_ += io.DeltaTime; + if (steady_ >= rest_seconds) { + rest_ = ImVec2(raw_x, raw_y); + rest_known_ = true; + } + } else { + steady_ = 0; + } + last_x_ = raw_x; + last_y_ = raw_y; + // Measured from where it rests, and not at all until that is known: a + // pointer creeping off on its own would take the screen from the focus. + const float x = raw_x - rest_.x, y = raw_y - rest_.y; const float tilt = std::min(1.0f, std::sqrt(x * x + y * y)); - const bool steering = tilt > feel.dead_zone; + const bool steering = rest_known_ && tilt > feel.dead_zone; // The first R3 only shows the pointer: a stray click must not press // whatever the mouse last rested on. const bool click = pointing_ && (pressed & right_thumb); diff --git a/Launcher/gamepad_input.h b/Launcher/gamepad_input.h index 1d66b05..b684aa5 100644 --- a/Launcher/gamepad_input.h +++ b/Launcher/gamepad_input.h @@ -54,6 +54,10 @@ private: ImGuiKey back_key_{ImGuiKey_Escape}; bool pointing_{}; bool clicking_{}; // R3 holds the left button + ImVec2 rest_{}; // where the right stick rests + bool rest_known_{}; + float steady_{}; // seconds the right stick held still + float last_x_{}, last_y_{}; ImVec2 pointer_{}; ImVec2 mouse_{-1, -1}; // where the last mouse move was };