diff --git a/runtime/src/settings_overlay.cpp b/runtime/src/settings_overlay.cpp index 616d141d8..ac9f098b8 100644 --- a/runtime/src/settings_overlay.cpp +++ b/runtime/src/settings_overlay.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #if defined(_WIN32) @@ -737,6 +738,54 @@ void DrawRumbleSettings() { } } +struct PollRate { + uint64_t startNs = 0; + uint32_t samples = 0; + int hz = 0; + bool ownsSensor = false; +}; +std::unordered_map g_pollRates; +bool g_pollRateWanted = false; +bool g_pollRateActive = false; + +// Accel reports arrive once per HID report; Wii Remotes are left to WiiRemoteInput, which owns their sensors. +void UpdatePollRateSensors() { + if (g_pollRateWanted) { + g_pollRateActive = true; + for (uint32_t index = 0; index < PADCount(); ++index) { + SDL_Gamepad* pad = PADGetSDLGamepadForIndex(index); + if (pad && SDL_GamepadHasSensor(pad, SDL_SENSOR_ACCEL) && + !SDL_GamepadSensorEnabled(pad, SDL_SENSOR_ACCEL) && + WiiRemoteInput::KindForName(SDL_GetGamepadName(pad)) == WiiRemoteInput::Kind::NotWii && + SDL_SetGamepadSensorEnabled(pad, SDL_SENSOR_ACCEL, true)) { + g_pollRates[SDL_GetGamepadID(pad)].ownsSensor = true; + } + } + return; + } + if (!g_pollRateActive) return; + g_pollRateActive = false; + for (const auto& [id, rate] : g_pollRates) { + if (SDL_Gamepad* pad = SDL_GetGamepadFromID(id); pad && rate.ownsSensor) { + SDL_SetGamepadSensorEnabled(pad, SDL_SENSOR_ACCEL, false); + } + } + g_pollRates.clear(); +} + +std::string PollRateLabel(uint32_t port) { + const s32 index = PADGetIndexForPort(port); + SDL_Gamepad* pad = index >= 0 ? PADGetSDLGamepadForIndex(static_cast(index)) : nullptr; + const auto it = pad ? g_pollRates.find(SDL_GetGamepadID(pad)) : g_pollRates.end(); + if (it == g_pollRates.end() || it->second.hz <= 0) return {}; + std::string label = std::to_string(it->second.hz) + " Hz"; + if (it->second.hz < 900 && SDL_GetGamepadType(pad) == SDL_GAMEPAD_TYPE_PS5 && + SDL_GetGamepadConnectionState(pad) == SDL_JOYSTICK_CONNECTION_WIRED) { + label += " - hidusbf can raise this to 1000 Hz"; + } + return label; +} + void DrawControllerSettings() { for (int port = 0; port < PAD_MAX_CONTROLLERS; ++port) { const std::string label = "Port " + std::to_string(port + 1); @@ -754,6 +803,11 @@ void DrawControllerSettings() { ImGui::Separator(); const char* currentName = PADGetName(selectedGamePort); ImGui::Text("Assigned: %s", currentName != nullptr ? currentName : "None"); + g_pollRateWanted = true; + if (const std::string rate = PollRateLabel(selectedGamePort); !rate.empty()) { + ImGui::SameLine(); + ImGui::TextDisabled("%s", rate.c_str()); + } if (ImGui::MenuItem("Unassign controller")) { PADClearPort(selectedGamePort); g_configuredControllerIndices.fill(std::numeric_limits::min()); @@ -1420,6 +1474,19 @@ void HandleEvents(const AuroraEvent* events) noexcept { if (IsMouseActivity(ev->sdl)) { g_lastMouseActivity = Clock::now(); } + if (g_pollRateActive && ev->sdl.type == SDL_EVENT_GAMEPAD_SENSOR_UPDATE && + ev->sdl.gsensor.sensor == SDL_SENSOR_ACCEL) { + PollRate& rate = g_pollRates[ev->sdl.gsensor.which]; + const uint64_t ns = ev->sdl.gsensor.sensor_timestamp; + if (rate.samples++ == 0 || ns <= rate.startNs) { + rate.startNs = ns; + rate.samples = 1; + } else if (ns - rate.startNs >= 250'000'000) { + rate.hz = static_cast(std::lround((rate.samples - 1) * 1e9 / static_cast(ns - rate.startNs))); + rate.startNs = ns; + rate.samples = 1; + } + } } } @@ -1453,6 +1520,7 @@ void Draw() noexcept { // its "communications interrupted" prompt without polling pads). Same guest // thread as PADRead, so no concurrent access to the scanner's state. WiiRemoteInput::Poll(); + g_pollRateWanted = false; ApplyConfiguredMappings(); PersistDisplayModeIfChanged(); UpdateCursorAutoHide(); @@ -1464,6 +1532,7 @@ void Draw() noexcept { DrawExitPrompt(); controller_mapping_wizard::Draw(); ApplyInputBlockState(); + UpdatePollRateSensors(); DrawStartupScreen(); }