mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 03:00:14 +02:00
Toggle the first-person camera with a right thumbstick click
Clicking the right thumbstick flips first person exactly as the F10 checkbox does, and saves it the same way. It works on the VR controllers in either presentation (with a short tick on the right controller) and on any other gamepad while VR is running. It never reached the game: SDL's stick click only lands in Aurora's extended buttons. A click counts on release, and only if the left thumbstick stayed up and the headset settings panel stayed closed throughout, so the two-thumbstick panel chord in gamepad mode never toggles the camera. A gamepad whose right stick click is bound to a GameCube control on its port, as a button or in an input expression, keeps it for the game. The XR side only posts a request; the toggle itself runs on the game thread with the checkbox. [vr] first_person_toggle_click (default true) and a checkbox under the camera toggle turn the click off. mkw_vr_camera_toggle_tests covers the click rule. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c11a7c9a61
commit
dd7f046214
11 files changed
+245
-4
No files matched your search
@@ -48,6 +48,7 @@ hud_virtual_screen = true
|
||||
stop_at_display_copy = true
|
||||
skip_copy_clears = true
|
||||
first_person = false
|
||||
first_person_toggle_click = true
|
||||
first_person_seat = "cockpit"
|
||||
cockpit_units_per_meter = 100.0
|
||||
first_person_units_per_meter = 50.0
|
||||
@@ -173,11 +174,12 @@ Touch controllers. The port is served through KPAD like a Bluetooth remote
|
||||
| Left trigger | Z |
|
||||
| Left grip | C |
|
||||
| Left Y | Settings panel (not a Wii button) |
|
||||
| Right stick click | First-person camera on / off (not a Wii button) |
|
||||
| Right controller motion and aim | Wii Remote accelerometer and pointer |
|
||||
| Left controller motion | Nunchuk accelerometer |
|
||||
|
||||
Analog inputs count as pressed past half travel. Right B, right stick left / right and the stick
|
||||
clicks are unbound, and no controller button presses HOME. The game's Wii Remote rumble vibrates
|
||||
Analog inputs count as pressed past half travel. Right B, right stick left / right and the left
|
||||
stick click are unbound, and no controller button presses HOME. The game's Wii Remote rumble vibrates
|
||||
both controllers, subject to the ordinary controller-vibration switch.
|
||||
|
||||
**Motion.** Each XR frame the aim and grip poses are located at the measured current time
|
||||
@@ -221,7 +223,7 @@ own motion detection still reads them, so a sharp enough turn can read as a shak
|
||||
A/B → South/East, X/Y → West/North, index triggers → trigger axes, grips → shoulders, thumbsticks
|
||||
→ sticks (clicks → stick buttons), left menu → Start. Every binding in the F10 controller menu
|
||||
applies. Left Y is GameCube Y here, so clicking both thumbsticks together opens the settings panel
|
||||
instead.
|
||||
instead. The right thumbstick click on its own still toggles the first-person camera.
|
||||
|
||||
Bindings are suggested for `oculus/touch_controller` (Quest 2, 3 and Pro) and
|
||||
`khr/simple_controller`. `mkw_vr_wii_remote_tests` checks the accelerometer frame, the pointer
|
||||
@@ -289,6 +291,17 @@ to the local driver's head instead, at one of two seats:
|
||||
|
||||
Both are a matter of taste rather than properties of the game, so the F10 bar exposes them.
|
||||
|
||||
**Toggling it from a controller.** Clicking the right thumbstick turns first person on or off
|
||||
exactly as the F10 checkbox does, and the choice is saved the same way. It works on the VR
|
||||
controllers in either presentation (the right controller gives a short tick), and on any other
|
||||
gamepad while VR is running. A click counts on release, and only if the left thumbstick stayed up
|
||||
and the settings panel stayed closed throughout, so clicking both thumbsticks to open the panel in
|
||||
gamepad mode never toggles the camera. A gamepad whose right thumbstick click is bound to a
|
||||
GameCube control on its port, as a button or in an input expression, keeps it for the game instead.
|
||||
Toggled in a menu, the change applies from the next race. `first_person_toggle_click = false`, or
|
||||
the F10 checkbox under the camera toggle, turns the click off. `mkw_vr_camera_toggle_tests` covers
|
||||
the click rule.
|
||||
|
||||
The kart is selected through the game's local-screen-to-racer mapping, including online races
|
||||
where your racer is not slot zero. First person requires a locally controlled racer; spectating
|
||||
another racer keeps the game's own camera.
|
||||
|
||||
@@ -410,7 +410,7 @@ add_test(NAME mkw_vr_first_person_tests COMMAND mkw_vr_first_person_tests)
|
||||
# The first-person cockpit (seated eye, wheel and handlebar geometry, level seat,
|
||||
# native wheel vertices) and hand steering (grab, turn, hand-off to the game),
|
||||
# ported from heurazy's mario-kart-wii-VR-port. All header-only.
|
||||
foreach(test_name mkw_steering_wheel_tests mkw_vr_cockpit_tests mkw_vr_hand_steering_tests)
|
||||
foreach(test_name mkw_steering_wheel_tests mkw_vr_cockpit_tests mkw_vr_hand_steering_tests mkw_vr_camera_toggle_tests)
|
||||
string(REGEX REPLACE "^mkw_" "" test_source "${test_name}")
|
||||
add_executable(${test_name} "${CMAKE_CURRENT_LIST_DIR}/tests/${test_source}.cpp")
|
||||
target_include_directories(${test_name} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/include")
|
||||
|
||||
@@ -63,6 +63,7 @@ struct RuntimeUserConfig {
|
||||
std::optional<std::string> vrControllerMode;
|
||||
std::optional<uint32_t> vrFrameInterpolationFps;
|
||||
std::optional<bool> vrFirstPerson;
|
||||
std::optional<bool> vrFirstPersonToggleClick;
|
||||
std::optional<float> vrFirstPersonUnitsPerMeter;
|
||||
std::optional<float> vrFirstPersonHeadUpMeters;
|
||||
std::optional<float> vrFirstPersonHeadForwardMeters;
|
||||
@@ -502,6 +503,9 @@ inline void EnsureConfigFile() {
|
||||
"# the kart, with the horizon kept level. Changeable live from the\n"
|
||||
"# F10 menu, and only during a single-screen race.\n"
|
||||
"first_person = false\n"
|
||||
"# Clicking the right thumbstick, on the VR controllers or on any\n"
|
||||
"# gamepad while VR runs, toggles first_person as the F10 checkbox does.\n"
|
||||
"first_person_toggle_click = true\n"
|
||||
"# Where the head sits: \"cockpit\" puts it at the driver's own eyes,\n"
|
||||
"# behind the steering wheel, at a life-size scale that allows for\n"
|
||||
"# the character's height, so the wheel is within reach.\n"
|
||||
@@ -742,6 +746,7 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
|
||||
config.vrStopAtDisplayCopy = FindConfigValue<bool>(document, "vr", "stop_at_display_copy");
|
||||
config.vrSkipCopyClears = FindConfigValue<bool>(document, "vr", "skip_copy_clears");
|
||||
config.vrFirstPerson = FindConfigValue<bool>(document, "vr", "first_person");
|
||||
config.vrFirstPersonToggleClick = FindConfigValue<bool>(document, "vr", "first_person_toggle_click");
|
||||
if (auto value = FindConfigFloat(document, "vr", "first_person_units_per_meter");
|
||||
value && *value >= 1.0f && *value <= 10000.0f) {
|
||||
config.vrFirstPersonUnitsPerMeter = *value;
|
||||
@@ -1066,6 +1071,11 @@ inline bool SetVrFirstPerson(bool value) {
|
||||
return WriteSetting("vr", "first_person", value ? "true" : "false");
|
||||
}
|
||||
|
||||
inline bool SetVrFirstPersonToggleClick(bool value) {
|
||||
Mutable().vrFirstPersonToggleClick = value;
|
||||
return WriteSetting("vr", "first_person_toggle_click", value ? "true" : "false");
|
||||
}
|
||||
|
||||
inline bool SetVrFirstPersonUnitsPerMeter(float value) {
|
||||
value = std::clamp(value, 1.0f, 10000.0f);
|
||||
Mutable().vrFirstPersonUnitsPerMeter = value;
|
||||
@@ -1510,6 +1520,10 @@ inline bool VrFirstPerson(bool fallback = false) {
|
||||
return Get().vrFirstPerson.value_or(fallback);
|
||||
}
|
||||
|
||||
inline bool VrFirstPersonToggleClick(bool fallback = true) {
|
||||
return Get().vrFirstPersonToggleClick.value_or(fallback);
|
||||
}
|
||||
|
||||
inline float VrFirstPersonUnitsPerMeter(float fallback = kVrFirstPersonUnitsPerMeterDefault) {
|
||||
return std::clamp(Get().vrFirstPersonUnitsPerMeter.value_or(fallback), 1.0f, 10000.0f);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@ void AdvancePresentedFrame() noexcept;
|
||||
// converted with the world scale currently in effect, so switching the
|
||||
// first-person camera on or off has to repeat it.
|
||||
void RefreshVrHudVirtualScreen() noexcept;
|
||||
// Flips the first-person camera exactly as its F10 checkbox does, on the game
|
||||
// thread at the next frame. Callable from any thread (the VR controllers'
|
||||
// right-thumbstick click).
|
||||
void RequestFirstPersonToggle() noexcept;
|
||||
// Put host controllers back to a neutral state before the process ends.
|
||||
void ReleaseControllers() noexcept;
|
||||
} // namespace settings_overlay
|
||||
@@ -0,0 +1,44 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
// The right thumbstick click that toggles the first-person camera, on the VR
|
||||
// controllers and on any gamepad while VR runs (F10 > VR >
|
||||
// first_person_toggle_click). Kept free of OpenXR and SDL so it is tested
|
||||
// headlessly (tests/vr_camera_toggle_tests.cpp).
|
||||
|
||||
namespace mkw::vr {
|
||||
|
||||
// One clean click of a button: it fires on release, and only if its partner
|
||||
// (the other thumbstick, which with it opens the headset settings panel on a
|
||||
// gamepad) stayed up and nothing owned the controllers at any point during the
|
||||
// press. Firing on release is what lets a two-stick chord pass untouched.
|
||||
class ClickToggle {
|
||||
public:
|
||||
bool Update(bool held, bool partner_held, bool blocked) noexcept {
|
||||
if (held) {
|
||||
if (!held_) {
|
||||
held_ = true;
|
||||
spoiled_ = partner_held || blocked;
|
||||
} else if (partner_held || blocked) {
|
||||
spoiled_ = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const bool fire = held_ && !spoiled_ && !blocked;
|
||||
held_ = false;
|
||||
spoiled_ = false;
|
||||
return fire;
|
||||
}
|
||||
void Reset() noexcept {
|
||||
held_ = false;
|
||||
spoiled_ = false;
|
||||
}
|
||||
bool Held() const noexcept { return held_; }
|
||||
|
||||
private:
|
||||
bool held_ = false;
|
||||
bool spoiled_ = false;
|
||||
};
|
||||
|
||||
} // namespace mkw::vr
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#if defined(MKW_ENABLE_OPENXR)
|
||||
|
||||
#include "vr/camera_toggle.h"
|
||||
#include "vr/openxr_driving.h"
|
||||
#include "vr/openxr_runtime.h"
|
||||
#include "vr/openxr_settings_panel.h"
|
||||
@@ -64,6 +65,9 @@ struct OpenXRPointerScreen {
|
||||
// the left stick's X axis in both presentations and that grip no longer reaches
|
||||
// the game (C on the Nunchuk, a shoulder on the gamepad).
|
||||
//
|
||||
// A right-thumbstick click on its own toggles the first-person camera, as its
|
||||
// F10 checkbox does (first_person_toggle_click).
|
||||
//
|
||||
// Left Y (both thumbsticks clicked together as a gamepad) opens the in-headset
|
||||
// settings panel (openxr_settings_panel.h). While it is open, and until every
|
||||
// button has been released after it closes, the game sees idle controllers: the
|
||||
@@ -166,6 +170,7 @@ private:
|
||||
bool m_haptics_active[kHands]{};
|
||||
uint32_t m_joystick_id = 0; // SDL_JoystickID; 0 when detached
|
||||
void* m_joystick = nullptr; // SDL_Joystick*
|
||||
ClickToggle m_first_person_click;
|
||||
SteeringWheel m_wheel;
|
||||
WheelReferenceLatch m_wheel_reference;
|
||||
driving::WheelVisual m_wheel_visual;
|
||||
|
||||
@@ -48,6 +48,9 @@ OpenXRControllerMode OpenXRGetControllerMode() noexcept;
|
||||
// Guest side. True when `sdl_joystick_id` is the OpenXR virtual gamepad and the
|
||||
// controllers are currently presented as a Wii Remote.
|
||||
bool OpenXRWiiRemoteOwnsGamepad(uint32_t sdl_joystick_id) noexcept;
|
||||
// True when `sdl_joystick_id` is the VR controllers' virtual gamepad, in either
|
||||
// presentation.
|
||||
bool OpenXRIsControllerGamepad(uint32_t sdl_joystick_id) noexcept;
|
||||
// Latest published sample; false before the first one or after withdrawal.
|
||||
bool OpenXRReadWiiRemote(OpenXRWiiRemoteSample& sample) noexcept;
|
||||
// WPADControlMotor for the emulated remote.
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "music_attenuation.h"
|
||||
#include "runtime_config.h"
|
||||
#include "runtime_log.h"
|
||||
#include "vr/camera_toggle.h"
|
||||
#include "vr/mkw_vr_first_person.h"
|
||||
#include "vr/mkw_vr_policy.h"
|
||||
#include "vr/openxr_diagnostics.h"
|
||||
@@ -33,6 +34,7 @@
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <unordered_map>
|
||||
#include <cctype>
|
||||
#include <cfloat>
|
||||
#include <charconv>
|
||||
@@ -140,6 +142,11 @@ bool g_vrStopAtDisplayCopy = RuntimeConfigFile::VrStopAtDisplayCopy(true);
|
||||
bool g_vrSkipCopyClears = RuntimeConfigFile::VrSkipCopyClears(true);
|
||||
bool g_vrHudVirtualScreen = RuntimeConfigFile::VrHudVirtualScreen(true);
|
||||
bool g_vrFirstPerson = RuntimeConfigFile::VrFirstPerson(false);
|
||||
bool g_vrFirstPersonToggleClick = RuntimeConfigFile::VrFirstPersonToggleClick();
|
||||
// Set from any thread by the right-thumbstick click, applied on the game thread.
|
||||
std::atomic<bool> g_firstPersonToggleRequested{false};
|
||||
// Per physical gamepad; the VR controllers keep theirs on the XR side.
|
||||
std::unordered_map<SDL_JoystickID, mkw::vr::ClickToggle> g_gamepadFirstPersonClicks;
|
||||
float g_vrFirstPersonUnitsPerMeter = RuntimeConfigFile::VrFirstPersonUnitsPerMeter();
|
||||
// 0 = cockpit, 1 = custom, matching kVrFirstPersonSeatNames.
|
||||
constexpr std::array<const char*, 2> kVrFirstPersonSeatNames{"cockpit", "custom"};
|
||||
@@ -1477,6 +1484,12 @@ void DrawVrSettings() {
|
||||
RuntimeConfigFile::SetVrFirstPerson(g_vrFirstPerson);
|
||||
mkw::vr::MkwVRFirstPersonApplyConfiguredSettings();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Also toggled by clicking the right thumbstick, on the VR controllers or on a gamepad.");
|
||||
}
|
||||
if (ImGui::Checkbox("Right thumbstick click toggles it", &g_vrFirstPersonToggleClick)) {
|
||||
RuntimeConfigFile::SetVrFirstPersonToggleClick(g_vrFirstPersonToggleClick);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"Moves the camera to the Player 1 driver's head and keeps the horizon level, "
|
||||
@@ -1614,6 +1627,65 @@ void DrawVrSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
// The right-thumbstick click: flips the first-person camera exactly as its
|
||||
// checkbox does. Game thread.
|
||||
void ToggleFirstPersonCamera() {
|
||||
g_vrFirstPerson = !g_vrFirstPerson;
|
||||
RuntimeConfigFile::SetVrFirstPerson(g_vrFirstPerson);
|
||||
mkw::vr::MkwVRFirstPersonApplyConfiguredSettings();
|
||||
RT_LOG(RT_TAG_RUNTIME) << "[mkw-vr] first-person camera " << (g_vrFirstPerson ? "on" : "off")
|
||||
<< " (right thumbstick click)" << std::endl;
|
||||
}
|
||||
|
||||
// A gamepad whose right thumbstick click reaches the game (bound to a
|
||||
// GameCube control on its port) keeps it; toggling the camera as well would
|
||||
// fire both.
|
||||
bool RightStickDrivesGame(SDL_Gamepad* gamepad) {
|
||||
if (gamepad == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (uint32_t port = 0; port < PAD_MAX_CONTROLLERS; ++port) {
|
||||
const s32 index = PADGetIndexForPort(port);
|
||||
if (index < 0 || PADGetSDLGamepadForIndex(static_cast<u32>(index)) != gamepad) {
|
||||
continue;
|
||||
}
|
||||
for (auto* mappings : {&PADGetButtonMappings, &PADGetAltButtonMappings}) {
|
||||
u32 count = 0;
|
||||
const PADButtonMapping* list = (*mappings)(port, &count);
|
||||
for (u32 i = 0; list != nullptr && i < count; ++i) {
|
||||
if (list[i].nativeButton == SDL_GAMEPAD_BUTTON_RIGHT_STICK) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (size_t control = 0; control < InputBindings::kControls.size(); ++control) {
|
||||
const std::string expression = InputBindings::GetExpression(port, control);
|
||||
if (expression.find("Thumb R") != std::string::npos || expression.find("Button 11") != std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// A physical gamepad's right-thumbstick click, while VR runs.
|
||||
void HandleGamepadFirstPersonClick(const SDL_GamepadButtonEvent& event) {
|
||||
const bool right = event.button == SDL_GAMEPAD_BUTTON_RIGHT_STICK;
|
||||
const bool left = event.button == SDL_GAMEPAD_BUTTON_LEFT_STICK;
|
||||
if ((!right && !left) || mkw::vr::OpenXRIsControllerGamepad(event.which)) {
|
||||
return;
|
||||
}
|
||||
SDL_Gamepad* gamepad = SDL_GetGamepadFromID(event.which);
|
||||
auto& click = g_gamepadFirstPersonClicks[event.which];
|
||||
const bool held = right ? event.down : click.Held();
|
||||
const bool partner = left ? event.down : gamepad != nullptr && SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_STICK);
|
||||
const bool blocked = g_rebind.active || InputBindings::InputBlocked();
|
||||
if (click.Update(held, partner, blocked) && g_vrFirstPersonToggleClick && mkw::vr::OpenXRIsRunning() &&
|
||||
!RightStickDrivesGame(gamepad)) {
|
||||
ToggleFirstPersonCamera();
|
||||
}
|
||||
}
|
||||
|
||||
// Export Logs. SDL shows the folder picker without blocking the game and calls
|
||||
// back on a thread of its choosing (its own dialog thread on Windows), where the
|
||||
// copy then runs; the menu only reads the outcome through this state.
|
||||
@@ -2217,6 +2289,8 @@ void InitializeRuntimeSettings() noexcept {
|
||||
|
||||
void RefreshVrHudVirtualScreen() noexcept { ApplyVrHudVirtualScreen(); }
|
||||
|
||||
void RequestFirstPersonToggle() noexcept { g_firstPersonToggleRequested.store(true, std::memory_order_release); }
|
||||
|
||||
void HandleEvents(const AuroraEvent* events) noexcept {
|
||||
if (!events) {
|
||||
return;
|
||||
@@ -2237,6 +2311,9 @@ void HandleEvents(const AuroraEvent* events) noexcept {
|
||||
CompleteRebind(g_rebind.kind == RebindKind::Controller ? PAD_NATIVE_BUTTON_DISABLED
|
||||
: static_cast<uint32_t>(PAD_KEY_INVALID));
|
||||
}
|
||||
if (ev->sdl.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN || ev->sdl.type == SDL_EVENT_GAMEPAD_BUTTON_UP) {
|
||||
HandleGamepadFirstPersonClick(ev->sdl.gbutton);
|
||||
}
|
||||
if (!g_rebind.active && IsToggleKey(ev->sdl, SDL_SCANCODE_F10)) {
|
||||
SetTopBarVisible(!g_topBarVisible);
|
||||
}
|
||||
@@ -2312,6 +2389,9 @@ 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();
|
||||
if (g_firstPersonToggleRequested.exchange(false, std::memory_order_acq_rel)) {
|
||||
ToggleFirstPersonCamera();
|
||||
}
|
||||
ApplyConfiguredMappings();
|
||||
PersistDisplayModeIfChanged();
|
||||
UpdateCursorAutoHide();
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "vr/openxr_input.h"
|
||||
#include "physical_wheel.h"
|
||||
#include "runtime_config.h"
|
||||
#include "settings_overlay.h"
|
||||
#include "vr/mkw_vr_first_person.h"
|
||||
#include "vr/openxr_diagnostics.h"
|
||||
|
||||
@@ -497,6 +498,7 @@ void OpenXRInput::Idle() {
|
||||
m_last_input_time = 0;
|
||||
m_panel_select_held = false;
|
||||
OpenXRPublishSettingsPanelPointer(false, 0.0f, 0.0f, false, 0.0f);
|
||||
m_first_person_click.Reset();
|
||||
ResetDriving();
|
||||
StopRumble();
|
||||
// Nothing stays held on the gamepad either while input is away.
|
||||
@@ -623,6 +625,17 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
|
||||
OpenXRSetSettingsPanelOpen(open);
|
||||
}
|
||||
|
||||
// A clean right-thumbstick click toggles the first-person camera. It fires
|
||||
// on release, so the two-thumbstick panel chord never toggles it, and
|
||||
// never while the panel has the controllers.
|
||||
if (m_first_person_click.Update(hands[1].thumbstick_click, hands[0].thumbstick_click,
|
||||
panel.open || panel.withheld) &&
|
||||
RuntimeConfigFile::VrFirstPersonToggleClick()) {
|
||||
settings_overlay::RequestFirstPersonToggle();
|
||||
constexpr XrDuration kToggleTickNs = 20'000'000;
|
||||
ApplyHaptic(1, 0.35f, kToggleTickNs);
|
||||
}
|
||||
|
||||
// The cockpit's wheel before the game reads the controllers: a held wheel
|
||||
// steers through the left stick and keeps its grips from the game.
|
||||
UpdateDriving(predicted_display_time, seat, hands, panel.withheld);
|
||||
|
||||
@@ -45,6 +45,10 @@ bool OpenXRWiiRemoteOwnsGamepad(uint32_t sdl_joystick_id) noexcept {
|
||||
wii_remote_bridge::g_joystick_id.load(std::memory_order_relaxed) == sdl_joystick_id;
|
||||
}
|
||||
|
||||
bool OpenXRIsControllerGamepad(uint32_t sdl_joystick_id) noexcept {
|
||||
return sdl_joystick_id != 0 && wii_remote_bridge::g_joystick_id.load(std::memory_order_relaxed) == sdl_joystick_id;
|
||||
}
|
||||
|
||||
bool OpenXRReadWiiRemote(OpenXRWiiRemoteSample& sample) noexcept {
|
||||
auto& published = wii_remote_bridge::Published();
|
||||
std::lock_guard lock(published.mutex);
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// The right-thumbstick click that toggles the first-person camera
|
||||
// (vr/camera_toggle.h).
|
||||
|
||||
#include "vr/camera_toggle.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace {
|
||||
|
||||
using mkw::vr::ClickToggle;
|
||||
|
||||
int g_failures = 0;
|
||||
|
||||
void Check(bool condition, const char* what) {
|
||||
if (!condition) {
|
||||
++g_failures;
|
||||
std::cerr << "FAILED: " << what << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
ClickToggle click;
|
||||
Check(!click.Update(true, false, false), "nothing on press");
|
||||
Check(!click.Update(true, false, false), "nothing while held");
|
||||
Check(click.Update(false, false, false), "a clean click fires on release");
|
||||
Check(!click.Update(false, false, false), "fires once");
|
||||
|
||||
// Both thumbsticks (the gamepad settings-panel chord), in either order.
|
||||
click.Update(true, false, false);
|
||||
click.Update(true, true, false);
|
||||
Check(!click.Update(false, true, false), "right then left: the chord, no toggle");
|
||||
click.Update(false, true, false);
|
||||
click.Update(true, true, false);
|
||||
Check(!click.Update(false, false, false), "left then right: the chord, no toggle");
|
||||
|
||||
// The panel owned the controllers during the press.
|
||||
click.Update(true, false, false);
|
||||
click.Update(true, false, true);
|
||||
Check(!click.Update(false, false, false), "blocked midway: no toggle");
|
||||
click.Update(true, false, false);
|
||||
Check(!click.Update(false, false, true), "blocked at release: no toggle");
|
||||
|
||||
// Recovers for the next clean click.
|
||||
click.Update(true, false, false);
|
||||
Check(click.Update(false, false, false), "a later clean click fires again");
|
||||
|
||||
click.Update(true, false, false);
|
||||
click.Reset();
|
||||
Check(!click.Update(false, false, false), "reset forgets a held press");
|
||||
|
||||
if (g_failures != 0) {
|
||||
std::cerr << g_failures << " check(s) failed\n";
|
||||
return 1;
|
||||
}
|
||||
std::cout << "vr camera toggle tests passed\n";
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user