mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 08:00:25 +02:00
Add support for "none" controller mode: unplug virtual joystick and allow alternative controllers
This commit is contained in:
1 parent
171702bef4
commit
c536f0087d
12 files changed
+212
-120
No files matched your search
@@ -203,10 +203,11 @@ int g_vrMirrorView = [] {
|
||||
}();
|
||||
// Config spellings and menu labels for the VR controllers, index-matched to
|
||||
// mkw::vr::OpenXRControllerMode.
|
||||
constexpr std::array<const char*, 2> kVrControllerModeNames{"wii_remote", "gamepad"};
|
||||
constexpr std::array<const char*, 2> kVrControllerModeLabels{"Wii Remote + Nunchuk", "Gamepad"};
|
||||
constexpr std::array<const char*, 3> kVrControllerModeNames{"wii_remote", "gamepad", "none"};
|
||||
constexpr std::array<const char*, 3> kVrControllerModeLabels{"Wii Remote + Nunchuk", "Gamepad", "None"};
|
||||
static_assert(static_cast<int>(mkw::vr::OpenXRControllerMode::WiiRemote) == 0);
|
||||
static_assert(static_cast<int>(mkw::vr::OpenXRControllerMode::Gamepad) == 1);
|
||||
static_assert(static_cast<int>(mkw::vr::OpenXRControllerMode::None) == 2);
|
||||
int g_vrControllerMode = [] {
|
||||
const std::string mode = RuntimeConfigFile::VrControllerMode();
|
||||
for (size_t i = 0; i < kVrControllerModeNames.size(); ++i) {
|
||||
@@ -878,9 +879,11 @@ void DrawVrControllerSettings() {
|
||||
"Wii Remote + Nunchuk: the right controller is a Wii Remote, with motion and a pointer "
|
||||
"that lands where you aim on the virtual screen; the left one is the Nunchuk.\n"
|
||||
" Right: A = A, trigger = B, B = C (look behind), stick up/down = 1/2\n"
|
||||
" Left: stick = Nunchuk stick, trigger = Z, X = -, menu = +, Y = settings panel\n"
|
||||
" Left: stick = Nunchuk stick, trigger = Z, X or menu = +, Y = settings panel\n"
|
||||
" The grips press nothing; they take hold of the wheel with hand steering.\n"
|
||||
"Gamepad: both controllers are one ordinary controller, read as a GameCube pad.\n"
|
||||
"None: the game does not see the VR controllers at all and they take no controller port,\n"
|
||||
"so a desktop gamepad or a Wii Remote plays instead. Left Y still opens the settings panel.\n"
|
||||
"Applies immediately; the game sees the controller change as a reconnection.");
|
||||
}
|
||||
if (mkw::vr::OpenXRIsRunning() &&
|
||||
@@ -2322,9 +2325,9 @@ void DrawVrSettingsPanelWindow() {
|
||||
}
|
||||
ImGui::TextDisabled("Aim and pull a trigger to change a setting, push a thumbstick to scroll.");
|
||||
ImGui::TextDisabled("%s to close. The game does not see the controllers meanwhile.",
|
||||
mkw::vr::OpenXRGetControllerMode() == mkw::vr::OpenXRControllerMode::WiiRemote
|
||||
? "Press left Y or Menu"
|
||||
: "Click both thumbsticks or press Menu");
|
||||
mkw::vr::OpenXRGetControllerMode() == mkw::vr::OpenXRControllerMode::Gamepad
|
||||
? "Click both thumbsticks or press Menu"
|
||||
: "Press left Y or Menu");
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginTabBar("Settings")) {
|
||||
const auto tab = [](const char* label, void (*draw)()) {
|
||||
|
||||
+132
-84
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
@@ -49,8 +50,15 @@ namespace {
|
||||
// slot that passes. So it leaves the gamepad here, and the game thread writes
|
||||
// it to SDL where it already polls controllers.
|
||||
//
|
||||
// m_sdl is the lock the SDL work runs under; the pacing thread only ever takes
|
||||
// m_state, and only for the copy. Both are taken in that order.
|
||||
// The relay also owns the virtual joystick. Attaching and detaching it take the
|
||||
// same lock, so a live switch to or from the None controller mode (which
|
||||
// unplugs it, so the VR controllers hold no port) is followed on the game
|
||||
// thread too; only the input's creation and destruction plug it in or out
|
||||
// where they run.
|
||||
//
|
||||
// m_sdl is the lock the SDL work, and the joystick, are under; the pacing
|
||||
// thread only ever takes m_state, and only for the copy. Both are taken in that
|
||||
// order.
|
||||
class VirtualGamepadRelay {
|
||||
public:
|
||||
struct Pad {
|
||||
@@ -58,18 +66,29 @@ public:
|
||||
std::array<bool, SDL_GAMEPAD_BUTTON_COUNT> buttons{};
|
||||
};
|
||||
|
||||
void Attach(SDL_Joystick* joystick) {
|
||||
// The controllers' input exists: plugs the joystick in unless the mode is
|
||||
// None. `logger` reports a joystick SDL refuses, from whichever thread.
|
||||
void Open(OpenXRLogCallback logger) {
|
||||
std::scoped_lock lock(m_sdl, m_state);
|
||||
m_joystick = joystick;
|
||||
m_logger = std::move(logger);
|
||||
m_open = true;
|
||||
m_attach_failed = false;
|
||||
m_pending = false;
|
||||
FollowControllerMode();
|
||||
}
|
||||
|
||||
void Detach() {
|
||||
// The controllers' input is going away: unplugs the joystick.
|
||||
void Close() {
|
||||
std::scoped_lock lock(m_sdl, m_state);
|
||||
m_joystick = nullptr;
|
||||
m_open = false;
|
||||
Detach();
|
||||
m_pending = false;
|
||||
m_logger = {};
|
||||
}
|
||||
|
||||
// The plugged-in joystick's SDL_JoystickID, 0 while unplugged.
|
||||
uint32_t JoystickId() const noexcept { return m_id.load(std::memory_order_relaxed); }
|
||||
|
||||
// Pacing thread.
|
||||
void Publish(const Pad& pad) {
|
||||
std::lock_guard lock(m_state);
|
||||
@@ -77,35 +96,106 @@ public:
|
||||
m_pending = true;
|
||||
}
|
||||
|
||||
// Game thread. Holding m_sdl here is what keeps Detach from closing the
|
||||
// Game thread. Holding m_sdl here is what keeps Close from closing the
|
||||
// joystick underneath the writes.
|
||||
void Apply() {
|
||||
std::lock_guard sdl(m_sdl);
|
||||
FollowControllerMode();
|
||||
Pad pad;
|
||||
SDL_Joystick* joystick = nullptr;
|
||||
{
|
||||
std::lock_guard lock(m_state);
|
||||
if (!m_pending || m_joystick == nullptr) {
|
||||
return;
|
||||
}
|
||||
pad = m_pad;
|
||||
joystick = m_joystick;
|
||||
m_pending = false;
|
||||
}
|
||||
for (int axis = 0; axis < SDL_GAMEPAD_AXIS_COUNT; ++axis) {
|
||||
SDL_SetJoystickVirtualAxis(joystick, static_cast<SDL_GamepadAxis>(axis),
|
||||
SDL_SetJoystickVirtualAxis(m_joystick, static_cast<SDL_GamepadAxis>(axis),
|
||||
pad.axes[static_cast<size_t>(axis)]);
|
||||
}
|
||||
for (int button = 0; button < SDL_GAMEPAD_BUTTON_COUNT; ++button) {
|
||||
SDL_SetJoystickVirtualButton(joystick, static_cast<SDL_GamepadButton>(button),
|
||||
SDL_SetJoystickVirtualButton(m_joystick, static_cast<SDL_GamepadButton>(button),
|
||||
pad.buttons[static_cast<size_t>(button)]);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// m_sdl held. A joystick SDL refused is not asked for again until the mode
|
||||
// unplugs it or the input is created anew.
|
||||
void FollowControllerMode() {
|
||||
if (!m_open) {
|
||||
return;
|
||||
}
|
||||
if (OpenXRGetControllerMode() == OpenXRControllerMode::None) {
|
||||
m_attach_failed = false;
|
||||
Detach();
|
||||
} else if (m_joystick == nullptr && !m_attach_failed) {
|
||||
m_attach_failed = !Attach();
|
||||
}
|
||||
}
|
||||
|
||||
bool Attach() {
|
||||
SDL_VirtualJoystickDesc desc;
|
||||
SDL_INIT_INTERFACE(&desc);
|
||||
desc.type = SDL_JOYSTICK_TYPE_GAMEPAD;
|
||||
desc.naxes = SDL_GAMEPAD_AXIS_COUNT;
|
||||
desc.nbuttons = SDL_GAMEPAD_BUTTON_COUNT;
|
||||
desc.button_mask = (1u << SDL_GAMEPAD_BUTTON_SOUTH) | (1u << SDL_GAMEPAD_BUTTON_EAST) |
|
||||
(1u << SDL_GAMEPAD_BUTTON_WEST) | (1u << SDL_GAMEPAD_BUTTON_NORTH) |
|
||||
(1u << SDL_GAMEPAD_BUTTON_START) | (1u << SDL_GAMEPAD_BUTTON_LEFT_STICK) |
|
||||
(1u << SDL_GAMEPAD_BUTTON_RIGHT_STICK) |
|
||||
(1u << SDL_GAMEPAD_BUTTON_LEFT_SHOULDER) |
|
||||
(1u << SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER);
|
||||
desc.axis_mask = (1u << SDL_GAMEPAD_AXIS_LEFTX) | (1u << SDL_GAMEPAD_AXIS_LEFTY) |
|
||||
(1u << SDL_GAMEPAD_AXIS_RIGHTX) | (1u << SDL_GAMEPAD_AXIS_RIGHTY) |
|
||||
(1u << SDL_GAMEPAD_AXIS_LEFT_TRIGGER) | (1u << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER);
|
||||
desc.name = "OpenXR Touch Controllers";
|
||||
const SDL_JoystickID id = SDL_AttachVirtualJoystick(&desc);
|
||||
if (id == 0) {
|
||||
Refused("SDL_AttachVirtualJoystick");
|
||||
return false;
|
||||
}
|
||||
SDL_Joystick* joystick = SDL_OpenJoystick(id);
|
||||
if (joystick == nullptr) {
|
||||
Refused("SDL_OpenJoystick");
|
||||
SDL_DetachVirtualJoystick(id);
|
||||
return false;
|
||||
}
|
||||
m_joystick = joystick;
|
||||
m_id.store(id, std::memory_order_relaxed);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Detach() {
|
||||
const SDL_JoystickID id = m_id.exchange(0, std::memory_order_relaxed);
|
||||
if (m_joystick != nullptr) {
|
||||
SDL_CloseJoystick(m_joystick);
|
||||
m_joystick = nullptr;
|
||||
}
|
||||
if (id != 0) {
|
||||
SDL_DetachVirtualJoystick(id);
|
||||
}
|
||||
}
|
||||
|
||||
void Refused(const char* operation) const {
|
||||
if (!m_logger) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
m_logger(OpenXRLogLevel::Warning, std::string(operation) + " failed: " + SDL_GetError() +
|
||||
"; OpenXR controllers will not reach the game");
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
std::mutex m_sdl;
|
||||
std::mutex m_state;
|
||||
OpenXRLogCallback m_logger;
|
||||
bool m_open = false;
|
||||
bool m_attach_failed = false;
|
||||
SDL_Joystick* m_joystick = nullptr;
|
||||
std::atomic<uint32_t> m_id{0};
|
||||
Pad m_pad;
|
||||
bool m_pending = false;
|
||||
};
|
||||
@@ -273,13 +363,18 @@ bool OpenXRInput::Create(OpenXRRuntime& runtime) {
|
||||
CreatePoseSpaces();
|
||||
LoadInputClock();
|
||||
LoadHandTracking();
|
||||
if (!AttachVirtualGamepad()) {
|
||||
Log(OpenXRLogLevel::Warning,
|
||||
"SDL refused the virtual gamepad; OpenXR controllers will not reach the game");
|
||||
Relay().Open(m_logger);
|
||||
switch (OpenXRGetControllerMode()) {
|
||||
case OpenXRControllerMode::WiiRemote:
|
||||
Log(OpenXRLogLevel::Info, "OpenXR controller actions attached (Wii Remote + Nunchuk)");
|
||||
break;
|
||||
case OpenXRControllerMode::Gamepad:
|
||||
Log(OpenXRLogLevel::Info, "OpenXR controller actions attached (gamepad)");
|
||||
break;
|
||||
case OpenXRControllerMode::None:
|
||||
Log(OpenXRLogLevel::Info, "OpenXR controller actions attached (none: the game does not see them)");
|
||||
break;
|
||||
}
|
||||
Log(OpenXRLogLevel::Info, OpenXRGetControllerMode() == OpenXRControllerMode::WiiRemote
|
||||
? "OpenXR controller actions attached (Wii Remote + Nunchuk)"
|
||||
: "OpenXR controller actions attached (gamepad)");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -739,54 +834,6 @@ void OpenXRApplyVirtualGamepad() noexcept {
|
||||
Relay().Apply();
|
||||
}
|
||||
|
||||
bool OpenXRInput::AttachVirtualGamepad() {
|
||||
SDL_VirtualJoystickDesc desc;
|
||||
SDL_INIT_INTERFACE(&desc);
|
||||
desc.type = SDL_JOYSTICK_TYPE_GAMEPAD;
|
||||
desc.naxes = SDL_GAMEPAD_AXIS_COUNT;
|
||||
desc.nbuttons = SDL_GAMEPAD_BUTTON_COUNT;
|
||||
desc.button_mask = (1u << SDL_GAMEPAD_BUTTON_SOUTH) | (1u << SDL_GAMEPAD_BUTTON_EAST) |
|
||||
(1u << SDL_GAMEPAD_BUTTON_WEST) | (1u << SDL_GAMEPAD_BUTTON_NORTH) |
|
||||
(1u << SDL_GAMEPAD_BUTTON_START) | (1u << SDL_GAMEPAD_BUTTON_LEFT_STICK) |
|
||||
(1u << SDL_GAMEPAD_BUTTON_RIGHT_STICK) |
|
||||
(1u << SDL_GAMEPAD_BUTTON_LEFT_SHOULDER) |
|
||||
(1u << SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER);
|
||||
desc.axis_mask = (1u << SDL_GAMEPAD_AXIS_LEFTX) | (1u << SDL_GAMEPAD_AXIS_LEFTY) |
|
||||
(1u << SDL_GAMEPAD_AXIS_RIGHTX) | (1u << SDL_GAMEPAD_AXIS_RIGHTY) |
|
||||
(1u << SDL_GAMEPAD_AXIS_LEFT_TRIGGER) | (1u << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER);
|
||||
desc.name = "OpenXR Touch Controllers";
|
||||
const SDL_JoystickID id = SDL_AttachVirtualJoystick(&desc);
|
||||
if (id == 0) {
|
||||
m_last_error = std::string("SDL_AttachVirtualJoystick failed: ") + SDL_GetError();
|
||||
Log(OpenXRLogLevel::Warning, m_last_error);
|
||||
return false;
|
||||
}
|
||||
SDL_Joystick* joystick = SDL_OpenJoystick(id);
|
||||
if (joystick == nullptr) {
|
||||
m_last_error = std::string("SDL_OpenJoystick failed: ") + SDL_GetError();
|
||||
Log(OpenXRLogLevel::Warning, m_last_error);
|
||||
SDL_DetachVirtualJoystick(id);
|
||||
return false;
|
||||
}
|
||||
m_joystick_id = id;
|
||||
m_joystick = joystick;
|
||||
Relay().Attach(joystick);
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenXRInput::DetachVirtualGamepad() {
|
||||
// Before the handle goes: the game thread may be writing through it.
|
||||
Relay().Detach();
|
||||
if (m_joystick != nullptr) {
|
||||
SDL_CloseJoystick(static_cast<SDL_Joystick*>(m_joystick));
|
||||
m_joystick = nullptr;
|
||||
}
|
||||
if (m_joystick_id != 0) {
|
||||
SDL_DetachVirtualJoystick(m_joystick_id);
|
||||
m_joystick_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenXRInput::Destroy() {
|
||||
// The game must stop reading a remote whose controllers are going away.
|
||||
OpenXRWithdrawWiiRemote();
|
||||
@@ -794,7 +841,7 @@ void OpenXRInput::Destroy() {
|
||||
if (m_created) {
|
||||
StopRumble();
|
||||
}
|
||||
DetachVirtualGamepad();
|
||||
Relay().Close();
|
||||
DestroyHandTrackers();
|
||||
m_create_hand_tracker = nullptr;
|
||||
m_destroy_hand_tracker = nullptr;
|
||||
@@ -842,7 +889,7 @@ void OpenXRInput::Idle() {
|
||||
}
|
||||
m_pointer.Reset();
|
||||
m_horizon = {1.0f, 0.0f};
|
||||
OpenXRPublishWiiRemote(m_joystick_id, OpenXRWiiRemoteSample{});
|
||||
OpenXRPublishWiiRemote(Relay().JoystickId(), OpenXRWiiRemoteSample{});
|
||||
// The panel stays as it was; only what the controllers were holding is forgotten.
|
||||
m_panel_controls.Reset();
|
||||
m_last_input_time = 0;
|
||||
@@ -855,9 +902,7 @@ void OpenXRInput::Idle() {
|
||||
ResetDriving();
|
||||
StopRumble();
|
||||
// Nothing stays held on the gamepad either while input is away.
|
||||
if (m_joystick != nullptr) {
|
||||
Relay().Publish({});
|
||||
}
|
||||
Relay().Publish({});
|
||||
}
|
||||
|
||||
void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen& screen,
|
||||
@@ -1015,8 +1060,11 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
|
||||
// is written back.
|
||||
const bool was_open = OpenXRSettingsPanelOpen();
|
||||
bool open = was_open;
|
||||
const settings_panel::Frame panel =
|
||||
m_panel_controls.Update(panel_hands, open, dt_seconds, OpenXRGetControllerMode());
|
||||
const OpenXRControllerMode mode = OpenXRGetControllerMode();
|
||||
const settings_panel::Frame panel = m_panel_controls.Update(panel_hands, open, dt_seconds, mode);
|
||||
// While the panel has the controllers, and always when they are nothing to
|
||||
// the game, the game sees them idle.
|
||||
const bool withheld = panel.withheld || mode == OpenXRControllerMode::None;
|
||||
// Pointer first: the game thread reads it as soon as it sees the panel open.
|
||||
PublishSettingsPanel(input_time, settings_panel, panel);
|
||||
if (open != was_open) {
|
||||
@@ -1036,7 +1084,7 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
|
||||
|
||||
// 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);
|
||||
UpdateDriving(predicted_display_time, seat, hands, withheld);
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
// Bare hands in the cockpit. While one of them holds the wheel it holds the
|
||||
@@ -1044,7 +1092,7 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
|
||||
// pause menu, the results, coasting) a right pinch stays A. The game's own
|
||||
// pointer cannot tell the two apart: MKW keeps it on while driving.
|
||||
const bool cockpit_hands =
|
||||
hand_tracking_on && m_driving.cockpit_active && m_driving.hand_steering && !panel.withheld;
|
||||
hand_tracking_on && m_driving.cockpit_active && m_driving.hand_steering && !withheld;
|
||||
std::array<bool, kHands> bare_held{};
|
||||
for (uint32_t hand = 0; hand < kHands; ++hand) {
|
||||
bare_held[hand] = cockpit_hands && m_bare_latch[hand].Bare() && m_wheel_held[hand];
|
||||
@@ -1069,7 +1117,7 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
|
||||
flick[hand] = {m_bare_latch[hand].Tracked(), m_wheel_held[hand],
|
||||
m_joint_frame.seat_from_joint[hand][hand_tracking::kPalm][7]};
|
||||
}
|
||||
if (m_flick.Update(flick, dt_seconds) && OpenXRGetControllerMode() == OpenXRControllerMode::WiiRemote) {
|
||||
if (m_flick.Update(flick, dt_seconds) && mode == OpenXRControllerMode::WiiRemote) {
|
||||
m_flick_start = input_time;
|
||||
}
|
||||
} else {
|
||||
@@ -1084,14 +1132,14 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
|
||||
m_injected_flick_held = injected_flick;
|
||||
#endif
|
||||
|
||||
// While the panel has the controllers, the game sees them idle.
|
||||
// What the game sees of the controllers: nothing held while they are withheld.
|
||||
static const std::array<wii_remote::HandInputs, kHands> kIdleHands{};
|
||||
const auto& game_hands = panel.withheld ? kIdleHands : hands;
|
||||
const auto& game_hands = withheld ? kIdleHands : hands;
|
||||
const wii_remote::HandInputs& left = game_hands[0];
|
||||
const wii_remote::HandInputs& right = game_hands[1];
|
||||
const auto injected = [&panel](const char* button) { return !panel.withheld && Injected(button); };
|
||||
const auto injected = [withheld](const char* button) { return !withheld && Injected(button); };
|
||||
|
||||
if (m_joystick != nullptr) {
|
||||
if (Relay().JoystickId() != 0) {
|
||||
VirtualGamepadRelay::Pad pad;
|
||||
// OpenXR thumbsticks report +Y up; SDL gamepads report +Y down.
|
||||
pad.axes[SDL_GAMEPAD_AXIS_LEFTX] = ToAxis(left.stick_x);
|
||||
@@ -1113,8 +1161,8 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
|
||||
Relay().Publish(pad);
|
||||
}
|
||||
|
||||
PublishWiiRemote(input_time, screen, game_hands, panel.withheld ? 0u : InjectedWiiRemoteButtons(),
|
||||
panel.withheld);
|
||||
PublishWiiRemote(input_time, screen, game_hands, withheld ? 0u : InjectedWiiRemoteButtons(),
|
||||
withheld);
|
||||
UpdateRumble();
|
||||
}
|
||||
|
||||
@@ -1215,7 +1263,7 @@ void OpenXRInput::PublishWiiRemote(XrTime input_time, const OpenXRPointerScreen&
|
||||
sample.nunchuk_acc = OpenXRWiiRemoteSample{}.nunchuk_acc;
|
||||
m_pointer.Reset();
|
||||
m_flick_start = 0;
|
||||
OpenXRPublishWiiRemote(m_joystick_id, sample);
|
||||
OpenXRPublishWiiRemote(Relay().JoystickId(), sample);
|
||||
return;
|
||||
}
|
||||
if (m_flick_start != 0) {
|
||||
@@ -1252,7 +1300,7 @@ void OpenXRInput::PublishWiiRemote(XrTime input_time, const OpenXRPointerScreen&
|
||||
Log(OpenXRLogLevel::Info, "OpenXR Wii Remote pointer reached the virtual screen");
|
||||
}
|
||||
}
|
||||
OpenXRPublishWiiRemote(m_joystick_id, sample);
|
||||
OpenXRPublishWiiRemote(Relay().JoystickId(), sample);
|
||||
}
|
||||
|
||||
void OpenXRInput::ResetDriving() {
|
||||
@@ -1427,7 +1475,7 @@ void OpenXRInput::UpdateDriving(XrTime display_time, const driving::SeatFrame& s
|
||||
}
|
||||
|
||||
void OpenXRInput::UpdateRumble() {
|
||||
if (!OpenXRWiiRemoteRumbleRequested() || !OpenXRWiiRemoteOwnsGamepad(m_joystick_id)) {
|
||||
if (!OpenXRWiiRemoteRumbleRequested() || !OpenXRWiiRemoteOwnsGamepad(Relay().JoystickId())) {
|
||||
StopRumble();
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user