vr: add OpenXR headset output, VR tracker and VR controller input behind --vr (#1726)

This commit is contained in:
Leandro Bras authored and nmzik committed 2026-10-20 02:18:51 +02:00
1 parent 0cad4672e9
commit 742fe17173
17 files changed
+1637 -28

No files matched your search

+158
View File
@@ -0,0 +1,158 @@
#include "common/emulatorConfig.h"
#include "common/logging/log.h"
#include "graphics/presentation/vrInput.h"
#include "kernel/pthread.h"
#include "libs/controller.h"
#include "libs/errno.h"
#include "libs/libs.h"
#include "libs/padData.h"
#include <algorithm>
#include <atomic>
#include <cstddef>
#include <cstring>
// Virtual headset only: the PS VR2 Sense controller ports. A headset runtime drives them when one
// is running; otherwise each hand reads its half of the first player's keyboard or gamepad.
namespace Libs::Controller {
LIB_NAME("Pad", "Pad");
constexpr int PAD_PORT_TYPE_VR_CONTROLLER_LEFT = 3;
// The buttons of each Sense controller. The left Create button is reserved for the system.
constexpr uint32_t HAND_BUTTONS[2] = {
PAD_BUTTON_L1 | PAD_BUTTON_L2 | PAD_BUTTON_L3 | PAD_BUTTON_TRIANGLE | PAD_BUTTON_SQUARE,
PAD_BUTTON_R1 | PAD_BUTTON_R2 | PAD_BUTTON_R3 | PAD_BUTTON_CIRCLE | PAD_BUTTON_CROSS |
PAD_BUTTON_OPTIONS};
struct PadVrControllerData {
uint32_t buttons;
uint8_t stick_x;
uint8_t stick_y;
uint8_t stick_padding[2];
uint8_t analog_l2;
uint8_t analog_r2;
uint8_t analog_padding[2];
uint32_t finger_touch;
uint8_t analog_finger_touch[4];
float acceleration[3];
float angular_velocity[3];
float orientation[4];
uint8_t reserve1[4];
bool connected;
uint8_t connected_count;
uint64_t timestamp;
uint8_t reserve2[24];
};
static_assert(sizeof(PadVrControllerData) == 104);
static_assert(offsetof(PadVrControllerData, finger_touch) == 12);
static_assert(offsetof(PadVrControllerData, acceleration) == 20);
static_assert(offsetof(PadVrControllerData, orientation) == 44);
static_assert(offsetof(PadVrControllerData, connected) == 64);
static_assert(offsetof(PadVrControllerData, timestamp) == 72);
static std::atomic<bool> g_open[2];
int PadVrPortHand(int type) {
const bool vr =
type == PAD_PORT_TYPE_VR_CONTROLLER_LEFT || type == PAD_PORT_TYPE_VR_CONTROLLER_LEFT + 1;
return vr && Config::VrEnabled() ? type - PAD_PORT_TYPE_VR_CONTROLLER_LEFT : -1;
}
int PadVrPort(int user_id, int hand, int index, bool open) {
if (!PadInitialized()) {
return PAD_ERROR_NOT_INITIALIZED;
}
const int handle = PAD_HANDLE_VR_LEFT + hand;
if (user_id != Config::GetUserId() || index != 0) {
return open ? PAD_ERROR_INVALID_ARG : PAD_ERROR_NO_HANDLE;
}
if (!open) {
return g_open[hand].load() ? handle : PAD_ERROR_NO_HANDLE;
}
return g_open[hand].exchange(true) ? PAD_ERROR_ALREADY_OPENED : handle;
}
int PadVrCheckHandle(int handle) {
const int hand = PadVrHand(handle);
return hand >= 0 && g_open[hand].load() ? OK : PAD_ERROR_INVALID_HANDLE;
}
int PadVrClose(int handle) {
const int hand = PadVrHand(handle);
return hand >= 0 && g_open[hand].exchange(false) ? OK : PAD_ERROR_INVALID_HANDLE;
}
static uint8_t ToByte(float value) {
return static_cast<uint8_t>(std::clamp(value, 0.0f, 1.0f) * 255.0f + 0.5f);
}
int KYTY_SYSV_ABI PadVrControllerReadState(int handle, PadVrControllerData* data) {
PRINT_NAME();
const int hand = PadVrHand(handle);
if (hand < 0) {
return PAD_ERROR_INVALID_HANDLE;
}
if (data == nullptr) {
return PAD_ERROR_INVALID_ARG;
}
PadData pad {};
if (const auto error = PadReadState(handle, &pad); error != OK) {
return error;
}
*data = {};
auto& trigger = hand == 0 ? data->analog_l2 : data->analog_r2;
data->buttons = pad.buttons & HAND_BUTTONS[hand];
data->stick_x = hand == 0 ? pad.left_stick_x : pad.right_stick_x;
data->stick_y = hand == 0 ? pad.left_stick_y : pad.right_stick_y;
trigger = hand == 0 ? pad.analog_buttons_l2 : pad.analog_buttons_r2;
std::memcpy(data->acceleration, &pad.acceleration_x, sizeof(data->acceleration));
std::memcpy(data->angular_velocity, &pad.angular_velocity_x, sizeof(data->angular_velocity));
std::memcpy(data->orientation, &pad.orientation_x, sizeof(data->orientation));
data->connected = pad.connected;
data->connected_count = pad.connected_count;
data->timestamp = pad.timestamp;
if (const auto xr = Vr::OpenXrGetController(hand); xr.connected) {
// Touch controllers have no button left for the right Options, so the left Menu stands in.
const bool options = hand == 1 && Vr::OpenXrGetController(0).menu;
// Each mapping names both hands; HAND_BUTTONS keeps this hand's half.
const uint32_t buttons = (xr.trigger > 0.5f ? PAD_BUTTON_L2 | PAD_BUTTON_R2 : 0u) |
(xr.squeeze > 0.5f ? PAD_BUTTON_L1 | PAD_BUTTON_R1 : 0u) |
(xr.stick_click ? PAD_BUTTON_L3 | PAD_BUTTON_R3 : 0u) |
(xr.primary ? PAD_BUTTON_SQUARE | PAD_BUTTON_CROSS : 0u) |
(xr.secondary ? PAD_BUTTON_TRIANGLE | PAD_BUTTON_CIRCLE : 0u) |
(options ? PAD_BUTTON_OPTIONS : 0u);
data->buttons = buttons & HAND_BUTTONS[hand];
data->stick_x = ToByte(xr.stick_x * 0.5f + 0.5f);
data->stick_y = ToByte(0.5f - xr.stick_y * 0.5f);
trigger = ToByte(xr.trigger);
if (xr.pose.valid) {
std::memcpy(data->orientation, xr.pose.orientation, sizeof(data->orientation));
}
data->connected = true;
data->connected_count = std::max<uint8_t>(data->connected_count, 1);
data->timestamp = LibKernel::KernelGetProcessTime();
}
return OK;
}
int KYTY_SYSV_ABI PadVrControllerSetTriggerEffects(int left_handle, int right_handle,
const PadTriggerEffectParam* param) {
PRINT_NAME();
LOGF("\t left_handle = %d\n"
"\t right_handle = %d\n",
left_handle, right_handle);
if (PadVrHand(left_handle) != 0 || PadVrCheckHandle(left_handle) != OK ||
PadVrHand(right_handle) != 1 || PadVrCheckHandle(right_handle) != OK) {
return PAD_ERROR_INVALID_HANDLE;
}
return param != nullptr ? OK : PAD_ERROR_INVALID_ARG;
}
} // namespace Libs::Controller