Add support for "none" controller mode: unplug virtual joystick and allow alternative controllers

This commit is contained in:
iChris4 committed 2026-09-26 21:53:48 +02:00
1 parent 171702bef4
commit c536f0087d
12 files changed
+212 -120

No files matched your search

+5 -4
View File
@@ -285,11 +285,12 @@ inline bool IsSupportedVrMirrorView(std::string_view value) {
}
// What the tracked VR controllers are to the game: "wii_remote" is a Wii
// Remote with a Nunchuk (motion and pointer included), "gamepad" one ordinary
// controller read as a GameCube pad. Matches mkw::vr::OpenXRControllerMode.
// controller read as a GameCube pad, "none" nothing at all (they only open the
// settings panel). Matches mkw::vr::OpenXRControllerMode.
inline constexpr const char* kVrControllerModeDefault = "wii_remote";
inline bool IsSupportedVrControllerMode(std::string_view value) {
return value == "wii_remote" || value == "gamepad";
return value == "wii_remote" || value == "gamepad" || value == "none";
}
// SDL scancode name, spelled the way SDL_GetScancodeName produces it. An
// empty string leaves the recenter hotkey unbound, menu button only.
@@ -510,8 +511,8 @@ inline void EnsureConfigFile() {
"# What the headset's controllers are to the game: \"wii_remote\"\n"
"# is a Wii Remote (right hand, with motion and a pointer aimed at\n"
"# the virtual screen) plus a Nunchuk (left hand); \"gamepad\" is one\n"
"# ordinary controller read as a GameCube pad. Changeable live from\n"
"# the F10 menu.\n"
"# ordinary controller read as a GameCube pad; \"none\" leaves the game\n"
"# to other controllers. Changeable live from the F10 menu.\n"
"controller_mode = \"wii_remote\"\n"
"# VR interpolation: 0 = Off, 1 = Auto, or 72/90/120 FPS. Live.\n"
"frame_interpolation_fps = 0\n"
+10 -8
View File
@@ -57,6 +57,11 @@ struct OpenXRPointerScreen {
// left / right thumbstick-> left / right stick axes, clicks -> stick buttons
// left menu -> Start
//
// None: the virtual joystick is unplugged, so the controllers hold no port and
// another controller (a desktop gamepad, a Bluetooth remote) plays in their
// place. The game sees them idle, the way it does while the settings panel has
// them; they still open the panel and toggle the first-person camera.
//
// Both are bound for the Oculus Touch profile; khr/simple_controller gets
// select/menu and the poses so an unknown runtime still offers something.
//
@@ -90,7 +95,8 @@ struct OpenXRPointerScreen {
// run on the XR pacing thread. The Wii Remote bridge is internally locked, so
// the game thread may read it concurrently, and the virtual gamepad is only
// published here: OpenXRApplyVirtualGamepad() performs the SDL writes on the
// game thread, keeping SDL's joystick lock off this thread entirely.
// game thread, and plugs the joystick in or out as the controller mode
// changes, keeping SDL's joystick lock off this thread during a session.
class OpenXRInput final {
public:
explicit OpenXRInput(OpenXRLogCallback logger = {});
@@ -131,7 +137,6 @@ public:
void ApplyHaptic(uint32_t hand, float amplitude, XrDuration duration);
bool IsCreated() const noexcept { return m_created; }
bool HasVirtualGamepad() const noexcept { return m_joystick_id != 0; }
const std::string& LastError() const noexcept { return m_last_error; }
private:
@@ -143,8 +148,6 @@ private:
void DestroyPoseSpaces();
void LoadInputClock();
XrTime InputSampleTime(XrTime predicted_display_time) const;
bool AttachVirtualGamepad();
void DetachVirtualGamepad();
// `withheld` publishes a remote at rest with nothing held and no pointer,
// while still tracking motion so releasing it does not read as a jolt.
void PublishWiiRemote(XrTime input_time, const OpenXRPointerScreen& screen,
@@ -197,8 +200,6 @@ private:
bool m_panel_select_held = false;
std::array<float, 2> m_horizon{1.0f, 0.0f};
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;
@@ -262,8 +263,9 @@ private:
std::string m_last_error;
};
// Game thread: writes the gamepad the pacing thread last published, if any.
// Does nothing when no OpenXR controllers are attached.
// Game thread: plugs the virtual gamepad in or out as the controller mode asks,
// then writes the gamepad the pacing thread last published, if any. Does
// nothing when no OpenXR controllers are attached.
void OpenXRApplyVirtualGamepad() noexcept;
} // namespace mkw::vr
+4 -3
View File
@@ -77,10 +77,11 @@ struct Frame {
};
// Whether the button that opens and closes the panel is held. As a Wii Remote
// that is left Y, which has no Wii button; as a gamepad left Y is GameCube Y, so
// both thumbsticks clicked together stand in for it.
// that is left Y, which has no Wii button, and with the controllers nothing to
// the game it is left Y too; as a gamepad left Y is GameCube Y, so both
// thumbsticks clicked together stand in for it.
inline bool ToggleHeld(const std::array<HandInputs, 2>& hands, OpenXRControllerMode mode) noexcept {
if (mode == OpenXRControllerMode::WiiRemote) {
if (mode != OpenXRControllerMode::Gamepad) {
return hands[0].secondary;
}
return hands[0].thumbstick_click && hands[1].thumbstick_click;
+8 -4
View File
@@ -24,6 +24,9 @@ enum class OpenXRControllerMode : uint8_t {
WiiRemote,
// One ordinary gamepad, read through PAD as a GameCube controller.
Gamepad,
// Nothing to the game: the virtual gamepad is unplugged, so the controllers
// hold no port. They still open the settings panel.
None,
};
struct OpenXRWiiRemoteSample {
@@ -40,7 +43,7 @@ struct OpenXRWiiRemoteSample {
float distance_meters = 0.0f;
};
// Live switch between the two presentations; the settings bar and the launch
// Live switch between the presentations; the settings bar and the launch
// configuration both go through it.
void OpenXRSetControllerMode(OpenXRControllerMode mode) noexcept;
OpenXRControllerMode OpenXRGetControllerMode() noexcept;
@@ -343,8 +346,10 @@ struct HandInputs {
// Adapted from DolphinXR's default "OpenXR Wii Remote" profile
// (Data/Sys/Profiles/Wiimote):
// right A -> A, right trigger -> B, right B -> C, right stick up/down -> 1/2,
// left X -> -, left menu -> +,
// left X and left menu -> +,
// left trigger -> Z, left stick -> Nunchuk stick.
// + is on left X as well because the PlayStation VR2's controllers give no
// usable left menu, and - has no button because Mario Kart Wii never reads it.
// HOME has no button; left Y opens the settings panel (openxr_settings_panel.h).
// The grips press nothing: they take hold of the wheel (openxr_driving.h), and C
// is the game's look-behind, which a hand on the wheel would otherwise hold down.
@@ -360,8 +365,7 @@ inline uint32_t RemoteButtons(const HandInputs& left, const HandInputs& right) n
press(right.secondary, kButtonC);
press(right.stick_y > kPressThreshold, kButtonOne);
press(right.stick_y < -kPressThreshold, kButtonTwo);
press(left.primary, kButtonMinus);
press(left.menu, kButtonPlus);
press(left.primary || left.menu, kButtonPlus);
press(left.trigger > kPressThreshold, kButtonZ);
return hold;
}