mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 06:00:25 +02:00
Refactor OpenXR controller input handling and update settings panel interactions
This commit is contained in:
1 parent
60c443654f
commit
e681294709
11 files changed
+163
-95
No files matched your search
@@ -143,26 +143,27 @@ to a port like any other. `controller_mode` decides what the game finds on that
|
|||||||
from **F10 > VR > VR controllers**; the game sees a change as a controller reconnection.
|
from **F10 > VR > VR controllers**; the game sees a change as a controller reconnection.
|
||||||
|
|
||||||
`"wii_remote"`, the default, presents them as a Wii Remote with a Nunchuk, the way DolphinXR's
|
`"wii_remote"`, the default, presents them as a Wii Remote with a Nunchuk, the way DolphinXR's
|
||||||
OpenXR Wii Remote does, including its default `OpenXR Wii Remote` profile for the Touch
|
OpenXR Wii Remote does, with buttons adapted from its default `OpenXR Wii Remote` profile for the
|
||||||
controllers. The port is served through KPAD like a Bluetooth remote (`wii_remote_input.cpp`), so
|
Touch controllers. The port is served through KPAD like a Bluetooth remote
|
||||||
`WPADProbe` reports a Nunchuk and the game runs its own Wii Remote + Nunchuk control scheme:
|
(`wii_remote_input.cpp`), so `WPADProbe` reports a Nunchuk and the game runs its own Wii Remote + Nunchuk control scheme:
|
||||||
|
|
||||||
| Controller | Wii |
|
| Controller | Wii |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Right A | A |
|
| Right A | A |
|
||||||
| Right trigger | B |
|
| Right trigger | B |
|
||||||
| Right stick up / down | 1 / 2 |
|
| Right stick up / down | 1 / 2 |
|
||||||
| Right stick left / right | − / + |
|
| Left X | − |
|
||||||
|
| Left menu | + |
|
||||||
| Left stick | Nunchuk stick |
|
| Left stick | Nunchuk stick |
|
||||||
| Left trigger | Z |
|
| Left trigger | Z |
|
||||||
| Left grip | C |
|
| Left grip | C |
|
||||||
| Left menu | HOME |
|
| Left Y | Settings panel (not a Wii button) |
|
||||||
| Right controller motion and aim | Wii Remote accelerometer and pointer |
|
| Right controller motion and aim | Wii Remote accelerometer and pointer |
|
||||||
| Left controller motion | Nunchuk accelerometer |
|
| Left controller motion | Nunchuk accelerometer |
|
||||||
|
|
||||||
Analog inputs count as pressed past half travel. Right B, left X/Y and the stick clicks are unbound,
|
Analog inputs count as pressed past half travel. Right B, right stick left / right and the stick
|
||||||
as in DolphinXR's profile. The game's Wii Remote rumble vibrates both controllers, subject to the
|
clicks are unbound, and no controller button presses HOME. The game's Wii Remote rumble vibrates
|
||||||
ordinary controller-vibration switch.
|
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
|
**Motion.** Each XR frame the aim and grip poses are located at the measured current time
|
||||||
(`XR_KHR_win32_convert_performance_counter_time`, `XR_KHR_convert_timespec_time` on Android), not
|
(`XR_KHR_win32_convert_performance_counter_time`, `XR_KHR_convert_timespec_time` on Android), not
|
||||||
@@ -192,13 +193,14 @@ cursor for 100 ms before it disappears, so tracking spikes during fast motion do
|
|||||||
Raw IR camera dots in `KPADGetUnifiedWpadStatus` stay invalid; the game reads the pointer from
|
Raw IR camera dots in `KPADGetUnifiedWpadStatus` stay invalid; the game reads the pointer from
|
||||||
`KPADStatus`.
|
`KPADStatus`.
|
||||||
|
|
||||||
**Settings in the headset.** Clicking both thumbsticks together opens the settings panel described
|
**Settings in the headset.** Left Y opens the settings panel described below; while it is open the
|
||||||
below; while it is open the controllers operate the panel and the game sees them idle.
|
controllers operate the panel and the game sees them idle.
|
||||||
|
|
||||||
`"gamepad"` keeps the controllers one ordinary gamepad read through PAD as a GameCube controller:
|
`"gamepad"` keeps the controllers one ordinary gamepad read through PAD as a GameCube controller:
|
||||||
A/B → South/East, X/Y → West/North, index triggers → trigger axes, grips → shoulders, thumbsticks
|
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
|
→ sticks (clicks → stick buttons), left menu → Start. Every binding in the F10 controller menu
|
||||||
applies.
|
applies. Left Y is GameCube Y here, so clicking both thumbsticks together opens the settings panel
|
||||||
|
instead.
|
||||||
|
|
||||||
Bindings are suggested for `oculus/touch_controller` (Quest 2, 3 and Pro) and
|
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
|
`khr/simple_controller`. `mkw_vr_wii_remote_tests` checks the accelerometer frame, the pointer
|
||||||
@@ -208,8 +210,9 @@ raycast and debounce, the picture placement and the button profile without a hea
|
|||||||
|
|
||||||
The F10 settings bar is only visible on the desktop window, so the same settings are also offered on
|
The F10 settings bar is only visible on the desktop window, so the same settings are also offered on
|
||||||
a panel inside the headset, in menus and during an immersive race alike, including on the Quest.
|
a panel inside the headset, in menus and during an immersive race alike, including on the Quest.
|
||||||
**Click both thumbsticks together** to open it, and again to close it; the left controller's menu
|
**Press left Y** to open it, and again to close it (with `controller_mode = "gamepad"`, **click both
|
||||||
button and the panel's *Close* button also close it. It can be opened from the desktop as well, with
|
thumbsticks together** instead); the left controller's menu button and the panel's *Close* button
|
||||||
|
also close it. It can be opened from the desktop as well, with
|
||||||
**F10 → VR → Show these settings in the headset**.
|
**F10 → VR → Show these settings in the headset**.
|
||||||
|
|
||||||
The panel has the F10 bar's menus as tabs (VR, Graphics, Controllers, Audio, Diagnostics) and a
|
The panel has the F10 bar's menus as tabs (VR, Graphics, Controllers, Audio, Diagnostics) and a
|
||||||
@@ -219,8 +222,8 @@ pointing. Changes apply exactly as they do from the F10 bar, and the two stay in
|
|||||||
|
|
||||||
While the panel is open, and until every button has been released after it closes, the game sees
|
While the panel is open, and until every button has been released after it closes, the game sees
|
||||||
the VR controllers idle: no buttons, no pointer and a remote at rest. Nothing reaches the game from
|
the VR controllers idle: no buttons, no pointer and a remote at rest. Nothing reaches the game from
|
||||||
the chord, the trigger that clicked *Close*, or the menu press that closed the panel. The game is
|
the panel button, the trigger that clicked *Close*, or the menu press that closed the panel. The
|
||||||
not paused, so a race carries on while you change settings. Other controllers (keyboard, desktop
|
game is not paused, so a race carries on while you change settings. Other controllers (keyboard, desktop
|
||||||
gamepads, Bluetooth remotes) are not affected.
|
gamepads, Bluetooth remotes) are not affected.
|
||||||
|
|
||||||
The panel sits centred on the virtual screen, three quarters of its width across (1.8 m with the
|
The panel sits centred on the virtual screen, three quarters of its width across (1.8 m with the
|
||||||
@@ -240,8 +243,8 @@ backends already submit carry it, so no extra swapchain or composition layer is
|
|||||||
ImGui backend keeps a single projection uniform, so the panel's pass is submitted on its own command
|
ImGui backend keeps a single projection uniform, so the panel's pass is submitted on its own command
|
||||||
buffer before the desktop's ImGui pass of the same frame is recorded.
|
buffer before the desktop's ImGui pass of the same frame is recorded.
|
||||||
|
|
||||||
`mkw_vr_settings_panel_tests` covers the chord, the release latch, selection, scrolling and the
|
`mkw_vr_settings_panel_tests` covers the panel button in both controller modes, the release latch,
|
||||||
canvas mapping; `gx_fifo_tests` covers where the panel lands in each eye.
|
selection, scrolling and the canvas mapping; `gx_fifo_tests` covers where the panel lands in each eye.
|
||||||
|
|
||||||
## The first-person camera
|
## The first-person camera
|
||||||
|
|
||||||
|
|||||||
@@ -252,7 +252,6 @@ class SettingsPage(
|
|||||||
info(R.string.controls_map_stick, activity.getString(R.string.controls_map_stick_value))
|
info(R.string.controls_map_stick, activity.getString(R.string.controls_map_stick_value))
|
||||||
info(R.string.controls_map_z, activity.getString(R.string.controls_map_z_value))
|
info(R.string.controls_map_z, activity.getString(R.string.controls_map_z_value))
|
||||||
info(R.string.controls_map_c, activity.getString(R.string.controls_map_c_value))
|
info(R.string.controls_map_c, activity.getString(R.string.controls_map_c_value))
|
||||||
info(R.string.controls_map_home, activity.getString(R.string.controls_map_home_value))
|
|
||||||
info(R.string.controls_map_pointer, activity.getString(R.string.controls_map_pointer_value))
|
info(R.string.controls_map_pointer, activity.getString(R.string.controls_map_pointer_value))
|
||||||
info(R.string.controls_map_panel, activity.getString(R.string.controls_map_panel_value))
|
info(R.string.controls_map_panel, activity.getString(R.string.controls_map_panel_value))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,7 +130,7 @@
|
|||||||
<string name="about_credit_title_dolphin">Dolphin</string>
|
<string name="about_credit_title_dolphin">Dolphin</string>
|
||||||
<string name="about_credit_title_logo">Logo</string>
|
<string name="about_credit_title_logo">Logo</string>
|
||||||
<string name="about_licence_title">GPL v3</string>
|
<string name="about_licence_title">GPL v3</string>
|
||||||
<string name="settings_apply_note">Changes apply the next time the game starts. While playing, click both thumbsticks together to open the in-headset settings.</string>
|
<string name="settings_apply_note">Changes apply the next time the game starts. While playing, press left Y to open the in-headset settings (click both thumbsticks together with Gamepad controllers).</string>
|
||||||
<string name="settings_running_note">The game is running, so these changes wait until it is closed and started again.</string>
|
<string name="settings_running_note">The game is running, so these changes wait until it is closed and started again.</string>
|
||||||
<string name="settings_write_failed">Could not save Config.toml.</string>
|
<string name="settings_write_failed">Could not save Config.toml.</string>
|
||||||
<string name="settings_custom_value">Custom (%1$s)</string>
|
<string name="settings_custom_value">Custom (%1$s)</string>
|
||||||
@@ -205,19 +205,17 @@
|
|||||||
<string name="controls_map_12">1 / 2</string>
|
<string name="controls_map_12">1 / 2</string>
|
||||||
<string name="controls_map_12_value">Right stick up / down</string>
|
<string name="controls_map_12_value">Right stick up / down</string>
|
||||||
<string name="controls_map_minus_plus">− / +</string>
|
<string name="controls_map_minus_plus">− / +</string>
|
||||||
<string name="controls_map_minus_plus_value">Right stick left / right</string>
|
<string name="controls_map_minus_plus_value">Left X / left menu</string>
|
||||||
<string name="controls_map_stick">Nunchuk stick</string>
|
<string name="controls_map_stick">Nunchuk stick</string>
|
||||||
<string name="controls_map_stick_value">Left stick</string>
|
<string name="controls_map_stick_value">Left stick</string>
|
||||||
<string name="controls_map_z">Z</string>
|
<string name="controls_map_z">Z</string>
|
||||||
<string name="controls_map_z_value">Left trigger</string>
|
<string name="controls_map_z_value">Left trigger</string>
|
||||||
<string name="controls_map_c">C</string>
|
<string name="controls_map_c">C</string>
|
||||||
<string name="controls_map_c_value">Left grip</string>
|
<string name="controls_map_c_value">Left grip</string>
|
||||||
<string name="controls_map_home">HOME</string>
|
|
||||||
<string name="controls_map_home_value">Left menu</string>
|
|
||||||
<string name="controls_map_pointer">Pointer and motion</string>
|
<string name="controls_map_pointer">Pointer and motion</string>
|
||||||
<string name="controls_map_pointer_value">Aim and move the right controller</string>
|
<string name="controls_map_pointer_value">Aim and move the right controller</string>
|
||||||
<string name="controls_map_panel">Settings panel</string>
|
<string name="controls_map_panel">Settings panel</string>
|
||||||
<string name="controls_map_panel_value">Click both thumbsticks</string>
|
<string name="controls_map_panel_value">Left Y</string>
|
||||||
|
|
||||||
<!-- Audio tab -->
|
<!-- Audio tab -->
|
||||||
<string name="section_audio">Volume</string>
|
<string name="section_audio">Volume</string>
|
||||||
|
|||||||
+1
-1
@@ -581,7 +581,7 @@ the app:
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `debug.wiicompiled.vtxpad 0` | Turns the stride padding off, to re-check a driver update |
|
| `debug.wiicompiled.vtxpad 0` | Turns the stride padding off, to re-check a driver update |
|
||||||
| `debug.wiicompiled.validation 1` | Keeps WebGPU validation and robustness on in release builds |
|
| `debug.wiicompiled.validation 1` | Keeps WebGPU validation and robustness on in release builds |
|
||||||
| `debug.wiicompiled.inject <n>:<button>` | Presses `a`, `b`, `x`, `y`, `start`, `up`, `down`, `left` or `right` for 12 XR frames each time `<n>` changes. As a Wii Remote, `x`/`y`/`start` are 1/2/+, the directions push the Nunchuk stick, and `home`, `c` and `z` also exist. `panel` clicks both thumbsticks, opening or closing the settings panel (see `OPENXR.md`) |
|
| `debug.wiicompiled.inject <n>:<button>` | Presses `a`, `b`, `x`, `y`, `start`, `up`, `down`, `left` or `right` for 12 XR frames each time `<n>` changes. As a Wii Remote, `x`/`y`/`start` are 1/2/+, the directions push the Nunchuk stick, and `home`, `c` and `z` also exist. `panel` presses the settings panel's button (left Y, or both thumbsticks as a gamepad), opening or closing it (see `OPENXR.md`) |
|
||||||
| `debug.wiicompiled.fpslog 1` | Logs the game's rendered frame rate every 5 s, with per-frame averages of the producer's waits for the frame worker's DONE and SEALED phases and of the worker's seal, permit wait, prepare and encode stretches. The compositor's `VrApi` log line gives headset FPS, `GPU%`, `CPU%`, clock levels and app GPU time (`App=`) |
|
| `debug.wiicompiled.fpslog 1` | Logs the game's rendered frame rate every 5 s, with per-frame averages of the producer's waits for the frame worker's DONE and SEALED phases and of the worker's seal, permit wait, prepare and encode stretches. The compositor's `VrApi` log line gives headset FPS, `GPU%`, `CPU%`, clock levels and app GPU time (`App=`) |
|
||||||
|
|
||||||
The injector makes headset tests possible with nobody wearing the headset.
|
The injector makes headset tests possible with nobody wearing the headset.
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ struct OpenXRPointerScreen {
|
|||||||
// poses are located at the measured current time, turned into both
|
// poses are located at the measured current time, turned into both
|
||||||
// accelerometers and the IR pointer (the right aim ray against the virtual
|
// accelerometers and the IR pointer (the right aim ray against the virtual
|
||||||
// screen the renderer is showing), and published through openxr_wii_remote.h.
|
// screen the renderer is showing), and published through openxr_wii_remote.h.
|
||||||
// Buttons follow DolphinXR's "OpenXR Wii Remote" profile (see RemoteButtons).
|
// Buttons are adapted from DolphinXR's "OpenXR Wii Remote" profile (see
|
||||||
|
// RemoteButtons).
|
||||||
// The game's rumble drives both controllers' haptics.
|
// The game's rumble drives both controllers' haptics.
|
||||||
//
|
//
|
||||||
// Gamepad: the virtual joystick is read through PAD as a GameCube controller,
|
// Gamepad: the virtual joystick is read through PAD as a GameCube controller,
|
||||||
@@ -55,10 +56,10 @@ struct OpenXRPointerScreen {
|
|||||||
// Both are bound for the Oculus Touch profile; khr/simple_controller gets
|
// Both are bound for the Oculus Touch profile; khr/simple_controller gets
|
||||||
// select/menu and the poses so an unknown runtime still offers something.
|
// select/menu and the poses so an unknown runtime still offers something.
|
||||||
//
|
//
|
||||||
// Clicking both thumbsticks opens the in-headset settings panel
|
// Left Y (both thumbsticks clicked together as a gamepad) opens the in-headset
|
||||||
// (openxr_settings_panel.h). While it is open, and until every button has been
|
// settings panel (openxr_settings_panel.h). While it is open, and until every
|
||||||
// released after it closes, the game sees idle controllers: the chord, the
|
// button has been released after it closes, the game sees idle controllers: the
|
||||||
// pointer and the triggers belong to the panel.
|
// panel button, the pointer and the triggers belong to the panel.
|
||||||
//
|
//
|
||||||
// Lifetime: Create after the session exists (attaches the action set, which
|
// Lifetime: Create after the session exists (attaches the action set, which
|
||||||
// OpenXR permits once per session), Sync once per xrWaitFrame, Idle while the
|
// OpenXR permits once per session), Sync once per xrWaitFrame, Idle while the
|
||||||
|
|||||||
@@ -18,10 +18,11 @@ namespace mkw::vr {
|
|||||||
// controllers, which are withheld from the game while it is open.
|
// controllers, which are withheld from the game while it is open.
|
||||||
//
|
//
|
||||||
// The XR pacing thread owns the controllers: it opens and closes the panel from
|
// The XR pacing thread owns the controllers: it opens and closes the panel from
|
||||||
// the controller chord, aims the pointer and publishes it here. The game thread
|
// the controllers' panel button, aims the pointer and publishes it here. The
|
||||||
// draws the panel, may close it (its Close button) or open it (the F10 bar), and
|
// game thread draws the panel, may close it (its Close button) or open it (the
|
||||||
// reads the pointer once per presented frame. Nothing in this header depends on
|
// F10 bar), and reads the pointer once per presented frame. Nothing in this
|
||||||
// OpenXR, so the settings overlay compiles the same in builds without it.
|
// header depends on OpenXR, so the settings overlay compiles the same in builds
|
||||||
|
// without it.
|
||||||
|
|
||||||
// The panel's canvas in ImGui pixels, drawn at twice the desktop menu's scale.
|
// The panel's canvas in ImGui pixels, drawn at twice the desktop menu's scale.
|
||||||
inline constexpr float kSettingsPanelWidthPixels = 1440.0f;
|
inline constexpr float kSettingsPanelWidthPixels = 1440.0f;
|
||||||
@@ -75,10 +76,20 @@ struct Frame {
|
|||||||
float wheel = 0.0f;
|
float wheel = 0.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
inline bool ToggleHeld(const std::array<HandInputs, 2>& hands, OpenXRControllerMode mode) noexcept {
|
||||||
|
if (mode == OpenXRControllerMode::WiiRemote) {
|
||||||
|
return hands[0].secondary;
|
||||||
|
}
|
||||||
|
return hands[0].thumbstick_click && hands[1].thumbstick_click;
|
||||||
|
}
|
||||||
|
|
||||||
// The controller side of the panel, one Update per XR frame:
|
// The controller side of the panel, one Update per XR frame:
|
||||||
//
|
//
|
||||||
// - Clicking both thumbsticks together opens or closes it. Nothing else opens
|
// - The panel button (ToggleHeld) opens or closes it. Nothing else opens it
|
||||||
// it from the controllers: every other button already means something to the
|
// from the controllers: every other button already means something to the
|
||||||
// game.
|
// game.
|
||||||
// - While it is open, the left menu button also closes it, both triggers and
|
// - While it is open, the left menu button also closes it, both triggers and
|
||||||
// the A / X buttons select, the thumbsticks scroll, and the hand whose trigger
|
// the A / X buttons select, the thumbsticks scroll, and the hand whose trigger
|
||||||
@@ -87,15 +98,16 @@ struct Frame {
|
|||||||
// press that closed the panel never lands in the game as well.
|
// press that closed the panel never lands in the game as well.
|
||||||
class Controls {
|
class Controls {
|
||||||
public:
|
public:
|
||||||
// `open` is the shared flag: the chord flips it, and the game thread may
|
// `open` is the shared flag: the panel button flips it, and the game thread
|
||||||
// have changed it since the last frame. `dt_seconds` is the time since the
|
// may have changed it since the last frame. `dt_seconds` is the time since
|
||||||
// previous frame.
|
// the previous frame; `mode` is how the game sees the controllers.
|
||||||
Frame Update(const std::array<HandInputs, 2>& hands, bool& open, float dt_seconds) noexcept {
|
Frame Update(const std::array<HandInputs, 2>& hands, bool& open, float dt_seconds,
|
||||||
const bool chord = hands[0].thumbstick_click && hands[1].thumbstick_click;
|
OpenXRControllerMode mode) noexcept {
|
||||||
if (chord && !m_chord_held) {
|
const bool toggle = ToggleHeld(hands, mode);
|
||||||
|
if (toggle && !m_toggle_held) {
|
||||||
open = !open;
|
open = !open;
|
||||||
}
|
}
|
||||||
m_chord_held = chord;
|
m_toggle_held = toggle;
|
||||||
|
|
||||||
if (open && hands[0].menu && !m_menu_held && m_was_open) {
|
if (open && hands[0].menu && !m_menu_held && m_was_open) {
|
||||||
open = false;
|
open = false;
|
||||||
@@ -152,7 +164,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_chord_held = false;
|
bool m_toggle_held = false;
|
||||||
bool m_menu_held = false;
|
bool m_menu_held = false;
|
||||||
std::array<bool, 2> m_trigger_held{};
|
std::array<bool, 2> m_trigger_held{};
|
||||||
bool m_was_open = false;
|
bool m_was_open = false;
|
||||||
|
|||||||
@@ -337,10 +337,12 @@ struct HandInputs {
|
|||||||
float stick_y = 0.0f; // +up
|
float stick_y = 0.0f; // +up
|
||||||
};
|
};
|
||||||
|
|
||||||
// DolphinXR's default "OpenXR Wii Remote" profile (Data/Sys/Profiles/Wiimote):
|
// Adapted from DolphinXR's default "OpenXR Wii Remote" profile
|
||||||
|
// (Data/Sys/Profiles/Wiimote):
|
||||||
// right A -> A, right trigger -> B, right stick up/down -> 1/2,
|
// right A -> A, right trigger -> B, right stick up/down -> 1/2,
|
||||||
// right stick left/right -> -/+, left menu -> HOME,
|
// left X -> -, left menu -> +,
|
||||||
// left grip -> C, left trigger -> Z, left stick -> Nunchuk stick.
|
// left grip -> C, left trigger -> Z, left stick -> Nunchuk stick.
|
||||||
|
// HOME has no button; left Y opens the settings panel (openxr_settings_panel.h).
|
||||||
inline uint32_t RemoteButtons(const HandInputs& left, const HandInputs& right) noexcept {
|
inline uint32_t RemoteButtons(const HandInputs& left, const HandInputs& right) noexcept {
|
||||||
uint32_t hold = 0;
|
uint32_t hold = 0;
|
||||||
const auto press = [&hold](bool held, uint32_t bit) {
|
const auto press = [&hold](bool held, uint32_t bit) {
|
||||||
@@ -352,9 +354,8 @@ inline uint32_t RemoteButtons(const HandInputs& left, const HandInputs& right) n
|
|||||||
press(right.trigger > kPressThreshold, kButtonB);
|
press(right.trigger > kPressThreshold, kButtonB);
|
||||||
press(right.stick_y > kPressThreshold, kButtonOne);
|
press(right.stick_y > kPressThreshold, kButtonOne);
|
||||||
press(right.stick_y < -kPressThreshold, kButtonTwo);
|
press(right.stick_y < -kPressThreshold, kButtonTwo);
|
||||||
press(right.stick_x < -kPressThreshold, kButtonMinus);
|
press(left.primary, kButtonMinus);
|
||||||
press(right.stick_x > kPressThreshold, kButtonPlus);
|
press(left.menu, kButtonPlus);
|
||||||
press(left.menu, kButtonHome);
|
|
||||||
press(left.squeeze > kPressThreshold, kButtonC);
|
press(left.squeeze > kPressThreshold, kButtonC);
|
||||||
press(left.trigger > kPressThreshold, kButtonZ);
|
press(left.trigger > kPressThreshold, kButtonZ);
|
||||||
return hold;
|
return hold;
|
||||||
|
|||||||
@@ -1240,7 +1240,8 @@ void DrawVrSettings() {
|
|||||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
|
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
|
||||||
ImGui::SetTooltip(
|
ImGui::SetTooltip(
|
||||||
"Opens these settings on a panel in front of you, in menus and races alike.\n"
|
"Opens these settings on a panel in front of you, in menus and races alike.\n"
|
||||||
"In the headset, clicking both thumbsticks together opens and closes it too.\n"
|
"In the headset, left Y opens and closes it too (with Gamepad VR controllers,\n"
|
||||||
|
"click both thumbsticks together instead).\n"
|
||||||
"Aim at it and pull a trigger to change a setting; push a thumbstick to scroll.\n"
|
"Aim at it and pull a trigger to change a setting; push a thumbstick to scroll.\n"
|
||||||
"While it is open the game does not see the VR controllers.");
|
"While it is open the game does not see the VR controllers.");
|
||||||
}
|
}
|
||||||
@@ -1253,8 +1254,8 @@ void DrawVrSettings() {
|
|||||||
ImGui::SetTooltip(
|
ImGui::SetTooltip(
|
||||||
"Wii Remote + Nunchuk: the right controller is a Wii Remote, with motion and a pointer "
|
"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"
|
"that lands where you aim on the virtual screen; the left one is the Nunchuk.\n"
|
||||||
" Right: A = A, trigger = B, stick up/down = 1/2, stick left/right = -/+\n"
|
" Right: A = A, trigger = B, stick up/down = 1/2\n"
|
||||||
" Left: stick = Nunchuk stick, trigger = Z, grip = C, menu = HOME\n"
|
" Left: stick = Nunchuk stick, trigger = Z, grip = C, X = -, menu = +, Y = settings panel\n"
|
||||||
"Gamepad: both controllers are one ordinary controller, read as a GameCube pad.\n"
|
"Gamepad: both controllers are one ordinary controller, read as a GameCube pad.\n"
|
||||||
"Applies immediately; the game sees the controller change as a reconnection.");
|
"Applies immediately; the game sees the controller change as a reconnection.");
|
||||||
}
|
}
|
||||||
@@ -1967,7 +1968,10 @@ void DrawVrSettingsPanelWindow() {
|
|||||||
mkw::vr::OpenXRSetSettingsPanelOpen(false);
|
mkw::vr::OpenXRSetSettingsPanelOpen(false);
|
||||||
}
|
}
|
||||||
ImGui::TextDisabled("Aim and pull a trigger to change a setting, push a thumbstick to scroll.");
|
ImGui::TextDisabled("Aim and pull a trigger to change a setting, push a thumbstick to scroll.");
|
||||||
ImGui::TextDisabled("Click both thumbsticks or press Menu to close. The game does not see the controllers meanwhile.");
|
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");
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
if (ImGui::BeginTabBar("Settings")) {
|
if (ImGui::BeginTabBar("Settings")) {
|
||||||
const auto tab = [](const char* label, void (*draw)()) {
|
const auto tab = [](const char* label, void (*draw)()) {
|
||||||
|
|||||||
@@ -40,9 +40,10 @@ namespace {
|
|||||||
// A new sequence number holds the button for kInjectHoldFrames XR frames.
|
// A new sequence number holds the button for kInjectHoldFrames XR frames.
|
||||||
// Buttons: a, b, x, y, start, up, down, left, right, and for the Wii Remote
|
// Buttons: a, b, x, y, start, up, down, left, right, and for the Wii Remote
|
||||||
// presentation also home, c and z (x/y/start press 1/2/+ there, and the
|
// presentation also home, c and z (x/y/start press 1/2/+ there, and the
|
||||||
// directions push the Nunchuk stick). `panel` clicks both thumbsticks, opening
|
// directions push the Nunchuk stick). `panel` presses the settings panel's
|
||||||
// or closing the settings panel, where `a` then selects. The property is unset
|
// button (left Y, or both thumbsticks for a gamepad), opening or closing it,
|
||||||
// in normal use, so this costs one property read every few frames.
|
// where `a` then selects. The property is unset in normal use, so this costs
|
||||||
|
// one property read every few frames.
|
||||||
constexpr uint32_t kInjectHoldFrames = 12;
|
constexpr uint32_t kInjectHoldFrames = 12;
|
||||||
constexpr uint32_t kInjectPollFrames = 4;
|
constexpr uint32_t kInjectPollFrames = 4;
|
||||||
|
|
||||||
@@ -594,6 +595,8 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
|
|||||||
m_last_input_time = input_time;
|
m_last_input_time = input_time;
|
||||||
std::array<wii_remote::HandInputs, kHands> panel_hands = hands;
|
std::array<wii_remote::HandInputs, kHands> panel_hands = hands;
|
||||||
if (Injected("panel")) {
|
if (Injected("panel")) {
|
||||||
|
// The panel button in either controller mode.
|
||||||
|
panel_hands[0].secondary = true;
|
||||||
panel_hands[0].thumbstick_click = true;
|
panel_hands[0].thumbstick_click = true;
|
||||||
panel_hands[1].thumbstick_click = true;
|
panel_hands[1].thumbstick_click = true;
|
||||||
}
|
}
|
||||||
@@ -604,7 +607,8 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
|
|||||||
// is written back.
|
// is written back.
|
||||||
const bool was_open = OpenXRSettingsPanelOpen();
|
const bool was_open = OpenXRSettingsPanelOpen();
|
||||||
bool open = was_open;
|
bool open = was_open;
|
||||||
const settings_panel::Frame panel = m_panel_controls.Update(panel_hands, open, dt_seconds);
|
const settings_panel::Frame panel =
|
||||||
|
m_panel_controls.Update(panel_hands, open, dt_seconds, OpenXRGetControllerMode());
|
||||||
// Pointer first: the game thread reads it as soon as it sees the panel open.
|
// Pointer first: the game thread reads it as soon as it sees the panel open.
|
||||||
PublishSettingsPanel(input_time, settings_panel, panel);
|
PublishSettingsPanel(input_time, settings_panel, panel);
|
||||||
if (open != was_open) {
|
if (open != was_open) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
//
|
//
|
||||||
// The in-headset settings panel's controller handling, tested without a
|
// The in-headset settings panel's controller handling, tested without a
|
||||||
// headset: the thumbstick chord that opens and closes it, the release latch that
|
// headset: the button that opens and closes it in each controller mode, the
|
||||||
// keeps a closing press out of the game, selection, scrolling, and where a hit
|
// release latch that keeps a closing press out of the game, selection,
|
||||||
// lands on the panel's canvas.
|
// scrolling, and where a hit lands on the panel's canvas.
|
||||||
|
|
||||||
#include "vr/openxr_settings_panel.h"
|
#include "vr/openxr_settings_panel.h"
|
||||||
|
|
||||||
@@ -32,6 +32,8 @@ void CheckNear(float actual, float expected, const char* what, float tolerance =
|
|||||||
}
|
}
|
||||||
|
|
||||||
constexpr float kDt = 1.0f / 90.0f;
|
constexpr float kDt = 1.0f / 90.0f;
|
||||||
|
constexpr OpenXRControllerMode kWiiRemote = OpenXRControllerMode::WiiRemote;
|
||||||
|
constexpr OpenXRControllerMode kGamepad = OpenXRControllerMode::Gamepad;
|
||||||
|
|
||||||
std::array<HandInputs, 2> Released() {
|
std::array<HandInputs, 2> Released() {
|
||||||
return {};
|
return {};
|
||||||
@@ -44,46 +46,80 @@ std::array<HandInputs, 2> Chord() {
|
|||||||
return hands;
|
return hands;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChordOpensAndClosesOnce() {
|
std::array<HandInputs, 2> LeftY() {
|
||||||
|
std::array<HandInputs, 2> hands{};
|
||||||
|
hands[0].secondary = true;
|
||||||
|
return hands;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LeftYOpensAndClosesOnceAsAWiiRemote() {
|
||||||
Controls controls;
|
Controls controls;
|
||||||
bool open = false;
|
bool open = false;
|
||||||
|
|
||||||
|
auto right_b = Released();
|
||||||
|
right_b[1].secondary = true;
|
||||||
|
Frame frame = controls.Update(right_b, open, kDt, kWiiRemote);
|
||||||
|
Check(!open && !frame.withheld, "right B neither opens nor withholds");
|
||||||
|
frame = controls.Update(Chord(), open, kDt, kWiiRemote);
|
||||||
|
Check(!open && !frame.withheld, "as a Wii Remote the thumbstick chord does not open the panel");
|
||||||
|
controls.Update(Released(), open, kDt, kWiiRemote);
|
||||||
|
|
||||||
|
frame = controls.Update(LeftY(), open, kDt, kWiiRemote);
|
||||||
|
Check(open && frame.open && frame.withheld, "left Y opens the panel");
|
||||||
|
frame = controls.Update(LeftY(), open, kDt, kWiiRemote);
|
||||||
|
Check(open, "holding left Y does not toggle again");
|
||||||
|
frame = controls.Update(Released(), open, kDt, kWiiRemote);
|
||||||
|
Check(open && frame.withheld, "the panel stays open and keeps the controllers after left Y is released");
|
||||||
|
|
||||||
|
frame = controls.Update(LeftY(), open, kDt, kWiiRemote);
|
||||||
|
Check(!open && !frame.open, "left Y closes the panel again");
|
||||||
|
Check(frame.withheld, "the closing press is still withheld from the game");
|
||||||
|
frame = controls.Update(Released(), open, kDt, kWiiRemote);
|
||||||
|
Check(!frame.withheld, "the game gets the controllers back once everything is released");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChordOpensAndClosesOnceAsAGamepad() {
|
||||||
|
Controls controls;
|
||||||
|
bool open = false;
|
||||||
|
|
||||||
|
Frame frame = controls.Update(LeftY(), open, kDt, kGamepad);
|
||||||
|
Check(!open && !frame.withheld, "as a gamepad left Y is GameCube Y, not the panel");
|
||||||
auto one = Released();
|
auto one = Released();
|
||||||
one[0].thumbstick_click = true;
|
one[0].thumbstick_click = true;
|
||||||
Frame frame = controls.Update(one, open, kDt);
|
frame = controls.Update(one, open, kDt, kGamepad);
|
||||||
Check(!open && !frame.withheld, "one thumbstick click alone neither opens nor withholds");
|
Check(!open && !frame.withheld, "one thumbstick click alone neither opens nor withholds");
|
||||||
|
|
||||||
frame = controls.Update(Chord(), open, kDt);
|
frame = controls.Update(Chord(), open, kDt, kGamepad);
|
||||||
Check(open && frame.open && frame.withheld, "clicking both thumbsticks opens the panel");
|
Check(open && frame.open && frame.withheld, "clicking both thumbsticks opens the panel");
|
||||||
frame = controls.Update(Chord(), open, kDt);
|
frame = controls.Update(Chord(), open, kDt, kGamepad);
|
||||||
Check(open, "holding the chord does not toggle again");
|
Check(open, "holding the chord does not toggle again");
|
||||||
frame = controls.Update(Released(), open, kDt);
|
frame = controls.Update(Released(), open, kDt, kGamepad);
|
||||||
Check(open && frame.withheld, "the panel stays open and keeps the controllers after the chord is released");
|
Check(open && frame.withheld, "the panel stays open and keeps the controllers after the chord is released");
|
||||||
|
|
||||||
frame = controls.Update(Chord(), open, kDt);
|
frame = controls.Update(Chord(), open, kDt, kGamepad);
|
||||||
Check(!open && !frame.open, "the chord closes the panel again");
|
Check(!open && !frame.open, "the chord closes the panel again");
|
||||||
Check(frame.withheld, "the closing chord is still withheld from the game");
|
Check(frame.withheld, "the closing chord is still withheld from the game");
|
||||||
frame = controls.Update(Released(), open, kDt);
|
frame = controls.Update(Released(), open, kDt, kGamepad);
|
||||||
Check(!frame.withheld, "the game gets the controllers back once everything is released");
|
Check(!frame.withheld, "the game gets the controllers back once everything is released");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuClosesAndItsPressStaysOutOfTheGame() {
|
void MenuClosesAndItsPressStaysOutOfTheGame() {
|
||||||
Controls controls;
|
Controls controls;
|
||||||
bool open = false;
|
bool open = false;
|
||||||
controls.Update(Chord(), open, kDt);
|
controls.Update(LeftY(), open, kDt, kWiiRemote);
|
||||||
controls.Update(Released(), open, kDt);
|
controls.Update(Released(), open, kDt, kWiiRemote);
|
||||||
|
|
||||||
auto menu = Released();
|
auto menu = Released();
|
||||||
menu[0].menu = true;
|
menu[0].menu = true;
|
||||||
Frame frame = controls.Update(menu, open, kDt);
|
Frame frame = controls.Update(menu, open, kDt, kWiiRemote);
|
||||||
Check(!open, "the left menu button closes an open panel");
|
Check(!open, "the left menu button closes an open panel");
|
||||||
frame = controls.Update(menu, open, kDt);
|
frame = controls.Update(menu, open, kDt, kWiiRemote);
|
||||||
Check(frame.withheld, "HOME held across the close does not reach the game");
|
Check(frame.withheld, "+ held across the close does not reach the game");
|
||||||
Check(!open, "a held menu button does not reopen the panel");
|
Check(!open, "a held menu button does not reopen the panel");
|
||||||
frame = controls.Update(Released(), open, kDt);
|
frame = controls.Update(Released(), open, kDt, kWiiRemote);
|
||||||
Check(!frame.withheld, "released, the controllers go back to the game");
|
Check(!frame.withheld, "released, the controllers go back to the game");
|
||||||
|
|
||||||
frame = controls.Update(menu, open, kDt);
|
frame = controls.Update(menu, open, kDt, kWiiRemote);
|
||||||
Check(!open && !frame.withheld, "with the panel closed the menu button is the game's");
|
Check(!open && !frame.withheld, "with the panel closed the menu button is the game's");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,49 +129,49 @@ void SelectWaitsForAReleaseAndFollowsTheTrigger() {
|
|||||||
// Opened from the settings bar while a trigger is held for the game.
|
// Opened from the settings bar while a trigger is held for the game.
|
||||||
auto trigger = Released();
|
auto trigger = Released();
|
||||||
trigger[1].trigger = 1.0f;
|
trigger[1].trigger = 1.0f;
|
||||||
controls.Update(trigger, open, kDt);
|
controls.Update(trigger, open, kDt, kWiiRemote);
|
||||||
open = true;
|
open = true;
|
||||||
Frame frame = controls.Update(trigger, open, kDt);
|
Frame frame = controls.Update(trigger, open, kDt, kWiiRemote);
|
||||||
Check(frame.open && !frame.select, "a trigger held from before the panel opened does not click");
|
Check(frame.open && !frame.select, "a trigger held from before the panel opened does not click");
|
||||||
frame = controls.Update(Released(), open, kDt);
|
frame = controls.Update(Released(), open, kDt, kWiiRemote);
|
||||||
Check(!frame.select, "nothing held, nothing selected");
|
Check(!frame.select, "nothing held, nothing selected");
|
||||||
frame = controls.Update(trigger, open, kDt);
|
frame = controls.Update(trigger, open, kDt, kWiiRemote);
|
||||||
Check(frame.select && frame.pointing_hand == 1, "a fresh right trigger selects and points with the right hand");
|
Check(frame.select && frame.pointing_hand == 1, "a fresh right trigger selects and points with the right hand");
|
||||||
|
|
||||||
auto left = Released();
|
auto left = Released();
|
||||||
left[0].trigger = 0.9f;
|
left[0].trigger = 0.9f;
|
||||||
controls.Update(Released(), open, kDt);
|
controls.Update(Released(), open, kDt, kWiiRemote);
|
||||||
frame = controls.Update(left, open, kDt);
|
frame = controls.Update(left, open, kDt, kWiiRemote);
|
||||||
Check(frame.select && frame.pointing_hand == 0, "pulling the left trigger hands the pointer to the left hand");
|
Check(frame.select && frame.pointing_hand == 0, "pulling the left trigger hands the pointer to the left hand");
|
||||||
|
|
||||||
auto button = Released();
|
auto button = Released();
|
||||||
button[1].primary = true;
|
button[1].primary = true;
|
||||||
controls.Update(Released(), open, kDt);
|
controls.Update(Released(), open, kDt, kWiiRemote);
|
||||||
frame = controls.Update(button, open, kDt);
|
frame = controls.Update(button, open, kDt, kWiiRemote);
|
||||||
Check(frame.select && frame.pointing_hand == 0, "A selects without moving the pointer to another hand");
|
Check(frame.select && frame.pointing_hand == 0, "A selects without moving the pointer to another hand");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThumbstickScrolls() {
|
void ThumbstickScrolls() {
|
||||||
Controls controls;
|
Controls controls;
|
||||||
bool open = true;
|
bool open = true;
|
||||||
controls.Update(Released(), open, kDt);
|
controls.Update(Released(), open, kDt, kWiiRemote);
|
||||||
|
|
||||||
auto small = Released();
|
auto small = Released();
|
||||||
small[1].stick_y = 0.2f;
|
small[1].stick_y = 0.2f;
|
||||||
Check(controls.Update(small, open, kDt).wheel == 0.0f, "a resting thumbstick does not scroll");
|
Check(controls.Update(small, open, kDt, kWiiRemote).wheel == 0.0f, "a resting thumbstick does not scroll");
|
||||||
|
|
||||||
auto up = Released();
|
auto up = Released();
|
||||||
up[1].stick_y = 1.0f;
|
up[1].stick_y = 1.0f;
|
||||||
CheckNear(controls.Update(up, open, 0.5f).wheel, kScrollStepsPerSecond * 0.1f,
|
CheckNear(controls.Update(up, open, 0.5f, kWiiRemote).wheel, kScrollStepsPerSecond * 0.1f,
|
||||||
"full deflection scrolls up at the full rate, with a long frame clamped");
|
"full deflection scrolls up at the full rate, with a long frame clamped");
|
||||||
auto down = Released();
|
auto down = Released();
|
||||||
down[0].stick_y = -1.0f;
|
down[0].stick_y = -1.0f;
|
||||||
down[1].stick_y = 0.3f;
|
down[1].stick_y = 0.3f;
|
||||||
Check(controls.Update(down, open, kDt).wheel < 0.0f, "the more deflected stick decides the direction");
|
Check(controls.Update(down, open, kDt, kWiiRemote).wheel < 0.0f, "the more deflected stick decides the direction");
|
||||||
|
|
||||||
bool closed = false;
|
bool closed = false;
|
||||||
Controls idle;
|
Controls idle;
|
||||||
Check(idle.Update(up, closed, kDt).wheel == 0.0f, "a closed panel does not scroll");
|
Check(idle.Update(up, closed, kDt, kWiiRemote).wheel == 0.0f, "a closed panel does not scroll");
|
||||||
}
|
}
|
||||||
|
|
||||||
void HitsMapOntoTheCanvas() {
|
void HitsMapOntoTheCanvas() {
|
||||||
@@ -175,7 +211,8 @@ void BridgeAccumulatesWheelUntilTaken() {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
ChordOpensAndClosesOnce();
|
LeftYOpensAndClosesOnceAsAWiiRemote();
|
||||||
|
ChordOpensAndClosesOnceAsAGamepad();
|
||||||
MenuClosesAndItsPressStaysOutOfTheGame();
|
MenuClosesAndItsPressStaysOutOfTheGame();
|
||||||
SelectWaitsForAReleaseAndFollowsTheTrigger();
|
SelectWaitsForAReleaseAndFollowsTheTrigger();
|
||||||
ThumbstickScrolls();
|
ThumbstickScrolls();
|
||||||
|
|||||||
@@ -247,24 +247,33 @@ void TestButtons() {
|
|||||||
Check(RemoteButtons(left, right) == kButtonOne, "right stick up is 1");
|
Check(RemoteButtons(left, right) == kButtonOne, "right stick up is 1");
|
||||||
right.stick_y = -0.9f;
|
right.stick_y = -0.9f;
|
||||||
Check(RemoteButtons(left, right) == kButtonTwo, "right stick down is 2");
|
Check(RemoteButtons(left, right) == kButtonTwo, "right stick down is 2");
|
||||||
|
right.stick_y = 0.3f;
|
||||||
|
Check(RemoteButtons(left, right) == 0, "a light push is no press");
|
||||||
right.stick_y = 0.0f;
|
right.stick_y = 0.0f;
|
||||||
right.stick_x = -0.9f;
|
right.stick_x = -0.9f;
|
||||||
Check(RemoteButtons(left, right) == kButtonMinus, "right stick left is -");
|
Check(RemoteButtons(left, right) == 0, "right stick left is unbound");
|
||||||
right.stick_x = 0.9f;
|
right.stick_x = 0.9f;
|
||||||
Check(RemoteButtons(left, right) == kButtonPlus, "right stick right is +");
|
Check(RemoteButtons(left, right) == 0, "right stick right is unbound");
|
||||||
right.stick_x = 0.3f;
|
|
||||||
Check(RemoteButtons(left, right) == 0, "a light push is no press");
|
|
||||||
|
|
||||||
right = {};
|
right = {};
|
||||||
|
left.primary = true;
|
||||||
left.menu = true;
|
left.menu = true;
|
||||||
|
Check(RemoteButtons(left, right) == (kButtonMinus | kButtonPlus), "left X and menu are - and +");
|
||||||
|
left = {};
|
||||||
left.squeeze = 0.8f;
|
left.squeeze = 0.8f;
|
||||||
left.trigger = 0.7f;
|
left.trigger = 0.7f;
|
||||||
Check(RemoteButtons(left, right) == (kButtonHome | kButtonC | kButtonZ), "left menu, grip, trigger are HOME, C, Z");
|
Check(RemoteButtons(left, right) == (kButtonC | kButtonZ), "left grip and trigger are C and Z");
|
||||||
// Left X and Y have no Wii button in the profile.
|
// Left Y is the settings panel's button, and nothing presses HOME.
|
||||||
left = {};
|
left = {};
|
||||||
left.primary = true;
|
|
||||||
left.secondary = true;
|
left.secondary = true;
|
||||||
Check(RemoteButtons(left, right) == 0, "left X/Y are unbound");
|
right.secondary = true;
|
||||||
|
right.thumbstick_click = true;
|
||||||
|
left.thumbstick_click = true;
|
||||||
|
Check(RemoteButtons(left, right) == 0, "left Y, right B and the stick clicks are unbound");
|
||||||
|
left = {};
|
||||||
|
right = {};
|
||||||
|
left.menu = true;
|
||||||
|
Check((RemoteButtons(left, right) & kButtonHome) == 0, "left menu is no longer HOME");
|
||||||
|
|
||||||
left.stick_x = 1.0f;
|
left.stick_x = 1.0f;
|
||||||
left.stick_y = 1.0f;
|
left.stick_y = 1.0f;
|
||||||
|
|||||||
Reference in new issue
Block a user