mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-10-20 13:36:47 -04:00
Store Wii calibration per remote (#299)
* Store Wii calibration per remote * show calibration state per port * tie calibration message to remote
This commit is contained in:
@@ -69,10 +69,11 @@ struct RuntimeUserConfig {
|
||||
// SDL's sensor frame (x right, y out of the button face, z towards the user).
|
||||
// SDL's Wii driver falls back to a nominal zero point when its read of the
|
||||
// remote's calibration block times out (common over Bluetooth), so this is
|
||||
// measured in the overlay with the remote at rest.
|
||||
std::optional<double> wiiAccelOffsetX;
|
||||
std::optional<double> wiiAccelOffsetY;
|
||||
std::optional<double> wiiAccelOffsetZ;
|
||||
// measured in the overlay with the remote at rest. Stored per remote under
|
||||
// [controller.wii_accel_offsets]; the old shared
|
||||
// wii_accel_offset_* keys are only detected, to report that they are ignored.
|
||||
std::map<std::string, std::array<double, 3>> wiiAccelOffsets;
|
||||
bool hasLegacyWiiAccelOffset = false;
|
||||
// Debugging aid: append every KPAD sample of the Bluetooth remote (raw and
|
||||
// corrected accelerometer, buttons) to wii_accel_trace.csv next to Config.toml.
|
||||
std::optional<bool> wiiAccelTrace;
|
||||
@@ -381,6 +382,28 @@ inline void AppendOverlayRoots(RuntimeUserConfig& config, const std::string& roo
|
||||
}
|
||||
|
||||
// Reads every supported setting out of a parsed Config.toml document.
|
||||
// [x, y, z] of finite numbers (integers allowed), else nullopt.
|
||||
inline std::optional<std::array<double, 3>> ParseAccelOffset(const toml::value& value) {
|
||||
if (!value.is_array() || value.as_array().size() != 3) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::array<double, 3> offset{};
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
const toml::value& element = value.as_array()[i];
|
||||
if (element.is_floating()) {
|
||||
offset[i] = element.as_floating();
|
||||
} else if (element.is_integer()) {
|
||||
offset[i] = static_cast<double>(element.as_integer());
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!std::isfinite(offset[i])) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
|
||||
RuntimeUserConfig config;
|
||||
|
||||
@@ -404,6 +427,13 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
|
||||
config.controllerExpressions[key] = value.as_string();
|
||||
}
|
||||
}
|
||||
if (section->contains("wii_accel_offsets") && section->at("wii_accel_offsets").is_table()) {
|
||||
for (const auto& [key, value] : section->at("wii_accel_offsets").as_table()) {
|
||||
if (const auto offset = ParseAccelOffset(value)) {
|
||||
config.wiiAccelOffsets[key] = *offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config.widescreen = FindConfigValue<bool>(document, "video", "widescreen");
|
||||
@@ -465,9 +495,9 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
|
||||
FindConfigValue<bool>(document, "audio", "attenuate_music_when_media_plays");
|
||||
config.wiiRemotes = FindConfigValue<bool>(document, "controller", "wii_remotes");
|
||||
config.wiiContinuousScan = FindConfigValue<bool>(document, "controller", "wii_continuous_scan");
|
||||
config.wiiAccelOffsetX = FindConfigValue<double>(document, "controller", "wii_accel_offset_x");
|
||||
config.wiiAccelOffsetY = FindConfigValue<double>(document, "controller", "wii_accel_offset_y");
|
||||
config.wiiAccelOffsetZ = FindConfigValue<double>(document, "controller", "wii_accel_offset_z");
|
||||
for (const char* key : {"wii_accel_offset_x", "wii_accel_offset_y", "wii_accel_offset_z"}) {
|
||||
config.hasLegacyWiiAccelOffset |= FindConfigValue<double>(document, "controller", key).value_or(0.0) != 0.0;
|
||||
}
|
||||
config.wiiAccelTrace = FindConfigValue<bool>(document, "controller", "wii_accel_trace");
|
||||
config.networkEnabled = FindConfigValue<bool>(document, "network", "enabled");
|
||||
config.discordPresenceEnabled = FindConfigValue<bool>(document, "discord", "enabled");
|
||||
@@ -842,12 +872,13 @@ inline bool SetWiiContinuousScanEnabled(bool value) {
|
||||
return WriteSetting("controller", "wii_continuous_scan", value ? "true" : "false");
|
||||
}
|
||||
|
||||
// Wii Remote accelerometer zero-point correction (g, SDL sensor frame); all zero
|
||||
// when the remote has not been calibrated.
|
||||
inline std::array<double, 3> WiiAccelOffset() {
|
||||
const RuntimeUserConfig& config = Get();
|
||||
return {config.wiiAccelOffsetX.value_or(0.0), config.wiiAccelOffsetY.value_or(0.0),
|
||||
config.wiiAccelOffsetZ.value_or(0.0)};
|
||||
// Stored correction of the remote identified by `remoteKey`, if it has one.
|
||||
inline std::optional<std::array<double, 3>> WiiAccelOffsetFor(const std::string& remoteKey) {
|
||||
const auto it = Get().wiiAccelOffsets.find(remoteKey);
|
||||
if (it == Get().wiiAccelOffsets.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// Whether to write the per-frame accelerometer trace (off unless asked for).
|
||||
@@ -855,26 +886,14 @@ inline bool WiiAccelTraceEnabled(bool fallback = false) {
|
||||
return Get().wiiAccelTrace.value_or(fallback);
|
||||
}
|
||||
|
||||
// True while a non-zero correction is stored ("Clear calibration" writes zeros).
|
||||
inline bool HasWiiAccelOffset() {
|
||||
const std::array<double, 3> offset = WiiAccelOffset();
|
||||
return offset[0] != 0.0 || offset[1] != 0.0 || offset[2] != 0.0;
|
||||
}
|
||||
|
||||
// Persists the accelerometer correction measured by the overlay's calibration.
|
||||
inline bool SetWiiAccelOffset(const std::array<double, 3>& offset) {
|
||||
Mutable().wiiAccelOffsetX = offset[0];
|
||||
Mutable().wiiAccelOffsetY = offset[1];
|
||||
Mutable().wiiAccelOffsetZ = offset[2];
|
||||
bool ok = true;
|
||||
const char* keys[3] = {"wii_accel_offset_x", "wii_accel_offset_y", "wii_accel_offset_z"};
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
// Always a float literal, so a whole-number offset does not come back as a TOML integer.
|
||||
std::ostringstream formatted;
|
||||
formatted << std::fixed << std::setprecision(4) << offset[i];
|
||||
ok = WriteSetting("controller", keys[i], formatted.str()) && ok;
|
||||
}
|
||||
return ok;
|
||||
// Persists the accelerometer correction of one remote. `remoteKey` must be a bare TOML key.
|
||||
inline bool SetWiiAccelOffsetFor(const std::string& remoteKey, const std::array<double, 3>& offset) {
|
||||
Mutable().wiiAccelOffsets[remoteKey] = offset;
|
||||
// Always float literals, so a whole-number offset does not come back as a TOML integer.
|
||||
std::ostringstream formatted;
|
||||
formatted << std::fixed << std::setprecision(4) << '[' << offset[0] << ", " << offset[1] << ", " << offset[2]
|
||||
<< ']';
|
||||
return WriteSetting("controller.wii_accel_offsets", remoteKey, formatted.str());
|
||||
}
|
||||
|
||||
// Target frame rate for frame interpolation, or 0 to disable it.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
struct PADStatus;
|
||||
|
||||
@@ -73,16 +75,20 @@ bool ReadKpadSample(uint32_t chan, KpadSample& sample);
|
||||
// no remote or SDL has not delivered a sample yet.
|
||||
bool ReadAccelDebug(uint32_t chan, float sdlG[3], float kpadAcc[3]);
|
||||
|
||||
// Residual zero-point trim on top of SDL's factory-calibrated decode. Expects the
|
||||
// remote lying still, buttons up; Poll() averages about a second and stores it.
|
||||
// Residual zero-point trim on top of SDL's factory-calibrated decode, stored per
|
||||
// physical remote under [controller.wii_accel_offsets]. Expects the remote lying
|
||||
// still, buttons up; Poll() averages about a second and stores it for that remote.
|
||||
void StartAccelCalibration(uint32_t chan);
|
||||
// Forgets the stored correction.
|
||||
void ClearAccelCalibration();
|
||||
bool IsAccelCalibrating();
|
||||
// Forgets the stored correction of the remote on `chan`.
|
||||
void ClearAccelCalibration(uint32_t chan);
|
||||
bool IsAccelCalibrating(uint32_t chan);
|
||||
// 0..1 while a calibration is collecting samples.
|
||||
float AccelCalibrationProgress();
|
||||
// Outcome of the last calibration attempt for the overlay, or nullptr.
|
||||
const char* AccelCalibrationMessage();
|
||||
// Correction in effect for the remote on `chan` (zeros when uncalibrated), or
|
||||
// nullopt when the port has no remote.
|
||||
std::optional<std::array<double, 3>> ReadAccelOffset(uint32_t chan);
|
||||
// Outcome of the last calibration attempt on `chan` for the overlay, or nullptr.
|
||||
const char* AccelCalibrationMessage(uint32_t chan);
|
||||
|
||||
// Reports "no controller" on the GameCube side for every port served through
|
||||
// KPAD, so the game never sees the same remote twice. Runs after aurora's PADRead.
|
||||
|
||||
Reference in New Issue
Block a user