mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-10-14 19:27:09 -04:00
Keep Steam alive with SteamVR registered, run SteamVR at the glasses' rate, keep the glasses lip on top
This commit is contained in:
1 parent
c8c62960f6
commit
a746d56dc8
5 files changed
+130
-21
No files matched your search
@@ -17,7 +17,6 @@ import androidx.compose.foundation.focusGroup
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.hoverable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsHoveredAsState
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraintsScope
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -204,9 +203,6 @@ internal fun BoxWithConstraintsScope.DropSheet(
|
||||
val narrow = LocalNarrowPane.current
|
||||
val chipFocus = remember(tabs.size) { List(tabs.size) { FocusRequester() } }
|
||||
val buttonFocus = remember { FocusRequester() }
|
||||
val buttonSrc = remember { MutableInteractionSource() }
|
||||
val buttonHovered by buttonSrc.collectIsHoveredAsState()
|
||||
var buttonFocused by remember { mutableStateOf(false) }
|
||||
val rightForButton = remember { booleanArrayOf(false) }
|
||||
var rest by remember { mutableStateOf<Rect?>(null) }
|
||||
val openState = rememberUpdatedState(state.open)
|
||||
@@ -277,8 +273,7 @@ internal fun BoxWithConstraintsScope.DropSheet(
|
||||
BackHandler(enabled = state.open) { close() }
|
||||
if (state.ride.isRunning || (state.shown && !state.open)) VeilRing()
|
||||
|
||||
val lipWanted = !state.open && (!state.shown || state.lipBack) && covered() < 0.5f &&
|
||||
(buttonFocused || buttonHovered || inputMode.inputMode == InputMode.Touch)
|
||||
val lipWanted = !state.open && (!state.shown || state.lipBack) && covered() < 0.5f
|
||||
LaunchedEffect(lipWanted) {
|
||||
if (lipWanted) {
|
||||
if (!state.lipBack) delay(Motion.ms(160).toLong())
|
||||
@@ -453,9 +448,7 @@ internal fun BoxWithConstraintsScope.DropSheet(
|
||||
.focusProperties { canFocus = enabled }
|
||||
.onFocusChanged {
|
||||
if (it.hasFocus && rightForButton[0]) { rightForButton[0] = false; if (openState.value) close() }
|
||||
buttonFocused = it.hasFocus
|
||||
}
|
||||
.hoverable(buttonSrc)
|
||||
.onPreviewKeyEvent { e ->
|
||||
val k = e.nativeKeyEvent
|
||||
if (k.keyCode != KeyEvent.KEYCODE_DPAD_DOWN && !(state.open && k.keyCode == KeyEvent.KEYCODE_DPAD_LEFT)) false
|
||||
|
||||
@@ -80,6 +80,12 @@ Added by DroidDeck:
|
||||
The `faccessat2` exit stop (termux 5ba8b95, for glibc's ENOSYS fallback on kernels before 5.8) and
|
||||
the `statx` one (emulation on kernels before 4.11) are dropped when the running kernel is newer.
|
||||
Host build: `ioctl` 31.6 -> 0.5 us; `stat`/`statx`/`faccessat2` about 44 -> 28 us (one stop).
|
||||
- `0015-robust-list-from-seccomp.patch` - Android's app policy refuses `set_robust_list` and
|
||||
`get_robust_list`. proot still answers the first with `ENOSYS` but keeps the head each thread
|
||||
offered (cleared on `execve`, none in a new task), and answers the second for the calling thread
|
||||
with it. SteamVR's `vrclient.so` asks for the head through libc's `syscall()`, which
|
||||
`libblsession.so`'s robust.c never sees: `libopenvr_api.so` opens it `RTLD_DEEPBIND`. Steam
|
||||
aborted on "futex robust_list not initialized by pthreads" as soon as SteamVR was registered.
|
||||
|
||||
Prototype (inert unless `PROOT_FASTPATH` is set; see `docs/development/proot-performance.md`):
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: DroidDeck <[email protected]>
|
||||
Date: Tue, 13 Oct 2026 00:00:00 +0000
|
||||
Subject: [PATCH] seccomp: answer get_robust_list with the head set_robust_list
|
||||
was refused for
|
||||
|
||||
diff --git a/src/execve/exit.c b/src/execve/exit.c
|
||||
index f32d3ec..b5475b3 100644
|
||||
--- a/src/execve/exit.c
|
||||
+++ b/src/execve/exit.c
|
||||
@@ -473,6 +473,8 @@ void translate_execve_exit(Tracee *tracee)
|
||||
talloc_set_name_const(tracee->exe, "$exe");
|
||||
}
|
||||
|
||||
+ tracee->robust_list = 0;
|
||||
+
|
||||
/* New processes have no heap. */
|
||||
if (talloc_reference_count(tracee->heap) >= 1) {
|
||||
talloc_unlink(tracee, tracee->heap);
|
||||
diff --git a/src/tracee/seccomp.c b/src/tracee/seccomp.c
|
||||
index 77d5b27..0af232b 100644
|
||||
--- a/src/tracee/seccomp.c
|
||||
+++ b/src/tracee/seccomp.c
|
||||
@@ -559,6 +559,24 @@ static int handle_seccomp_event_common(Tracee *tracee)
|
||||
}
|
||||
|
||||
case PR_set_robust_list:
|
||||
+ tracee->robust_list = peek_reg(tracee, CURRENT, SYSARG_1);
|
||||
+ set_result_after_seccomp(tracee, -ENOSYS);
|
||||
+ break;
|
||||
+
|
||||
+ case PR_get_robust_list:
|
||||
+ {
|
||||
+ pid_t pid = (pid_t) peek_reg(tracee, CURRENT, SYSARG_1);
|
||||
+ if (tracee->robust_list == 0 || (pid != 0 && pid != tracee->pid)) {
|
||||
+ set_result_after_seccomp(tracee, -ENOSYS);
|
||||
+ break;
|
||||
+ }
|
||||
+ poke_word(tracee, peek_reg(tracee, CURRENT, SYSARG_2), tracee->robust_list);
|
||||
+ if (errno == 0)
|
||||
+ poke_word(tracee, peek_reg(tracee, CURRENT, SYSARG_3), 3 * sizeof_word(tracee));
|
||||
+ set_result_after_seccomp(tracee, errno != 0 ? -EFAULT : 0);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
default:
|
||||
/* Set errno to -ENOSYS */
|
||||
set_result_after_seccomp(tracee, -ENOSYS);
|
||||
diff --git a/src/tracee/tracee.h b/src/tracee/tracee.h
|
||||
index 1fca85f..496ca75 100644
|
||||
--- a/src/tracee/tracee.h
|
||||
+++ b/src/tracee/tracee.h
|
||||
@@ -248,6 +248,8 @@ typedef struct tracee {
|
||||
* has to be ignored as it's same syscall entry */
|
||||
bool seccomp_already_handled_enter;
|
||||
|
||||
+ word_t robust_list;
|
||||
+
|
||||
/**********************************************************************
|
||||
* Shared or private resources, depending on the CLONE_FS/VM flags. *
|
||||
**********************************************************************/
|
||||
@@ -464,6 +464,7 @@ public:
|
||||
EVRInitError Activate(uint32_t unObjectId) override {
|
||||
id_ = unObjectId;
|
||||
PropertyContainerHandle_t c = VRProperties()->TrackedDeviceToPropertyContainer(id_);
|
||||
container_ = c;
|
||||
width_ = VRSettings()->GetInt32(kSettingsSection, "windowWidth");
|
||||
height_ = VRSettings()->GetInt32(kSettingsSection, "windowHeight");
|
||||
if (width_ <= 0) width_ = 1920;
|
||||
@@ -480,6 +481,7 @@ public:
|
||||
}
|
||||
if (pose_.Read(&hp) && hp.display_hz >= 24.0f) hz = hp.display_hz;
|
||||
Vsync::SetRate(hz);
|
||||
sentHz_ = hz;
|
||||
sentTan_ = tanHalf_;
|
||||
sentAspect_ = eyeAspect_;
|
||||
VRProperties()->SetStringProperty(c, Prop_ModelNumber_String, "DroidDeck Cardboard");
|
||||
@@ -509,7 +511,7 @@ public:
|
||||
VRProperties()->SetBoolProperty(c, Prop_ContainsProximitySensor_Bool, true);
|
||||
VRDriverInput()->CreateBooleanComponent(c, "/proximity", &proximity_);
|
||||
VRDriverInput()->UpdateBooleanComponent(proximity_, true, 0);
|
||||
Log("HMD activated: %dx%d at %.0f Hz, eye tan(half fov) %.3f, eye aspect %.3f", width_, height_, hz, tanHalf_, eyeAspect_);
|
||||
Log("HMD activated: %dx%d at %.0f Hz, eye tan(half fov) %.3f, eye aspect %.3f", width_, height_, hz, tanHalf_.load(), eyeAspect_.load());
|
||||
|
||||
running_ = true;
|
||||
thread_ = std::thread([this] { PoseLoop(); });
|
||||
@@ -565,8 +567,9 @@ public:
|
||||
bool IsDisplayRealDisplay() override { return false; }
|
||||
void GetRecommendedRenderTargetSize(uint32_t *w, uint32_t *h) override {
|
||||
// Rendered a little larger than the panel: the lens pass magnifies the middle.
|
||||
*w = (uint32_t)(height_ * 1.4f * eyeAspect_);
|
||||
*h = (uint32_t)(height_ * 1.4f);
|
||||
const float scale = renderScale_;
|
||||
*w = (uint32_t)(height_ * scale * eyeAspect_);
|
||||
*h = (uint32_t)(height_ * scale);
|
||||
}
|
||||
void GetEyeOutputViewport(EVREye eye, uint32_t *x, uint32_t *y, uint32_t *w, uint32_t *h) override {
|
||||
*y = 0; *w = (uint32_t)width_ / 2; *h = (uint32_t)height_;
|
||||
@@ -592,6 +595,7 @@ private:
|
||||
void UpdateEye(const droiddeck_headpose &hp) {
|
||||
sentTan_ = tanHalf_ = hp.tan_half_fov;
|
||||
sentAspect_ = eyeAspect_ = hp.eye_aspect;
|
||||
renderScale_ = (hp.flags & DROIDDECK_HEADPOSE_MONO) ? 1.0f : 1.4f;
|
||||
HmdRect2_t r;
|
||||
r.vTopLeft.v[0] = -tanHalf_ * eyeAspect_;
|
||||
r.vTopLeft.v[1] = -tanHalf_;
|
||||
@@ -601,10 +605,17 @@ private:
|
||||
uint32_t w = 0, h = 0;
|
||||
GetRecommendedRenderTargetSize(&w, &h);
|
||||
VRServerDriverHost()->SetRecommendedRenderTargetSize(id_, w, h);
|
||||
Log("eye now tan(half fov) %.3f, aspect %.3f (%ux%u eye%s), render %ux%u", tanHalf_, eyeAspect_, hp.eye_w, hp.eye_h,
|
||||
Log("eye now tan(half fov) %.3f, aspect %.3f (%ux%u eye%s), render %ux%u", tanHalf_.load(), eyeAspect_.load(), hp.eye_w, hp.eye_h,
|
||||
(hp.flags & DROIDDECK_HEADPOSE_MONO) ? ", one eye shown" : "", w, h);
|
||||
}
|
||||
|
||||
void UpdateRate(float hz) {
|
||||
sentHz_ = hz;
|
||||
Vsync::SetRate(hz);
|
||||
VRProperties()->SetFloatProperty(container_, Prop_DisplayFrequency_Float, hz);
|
||||
Log("panel now %.0f Hz", hz);
|
||||
}
|
||||
|
||||
void PoseLoop() {
|
||||
uint32_t lastSeq = 0;
|
||||
auto lastSent = std::chrono::steady_clock::now();
|
||||
@@ -622,7 +633,7 @@ private:
|
||||
if (hp.eye_w > 0 && hp.tan_half_fov > 0.0f && hp.eye_aspect > 0.0f &&
|
||||
(fabsf(hp.tan_half_fov - sentTan_) > 1e-3f || fabsf(hp.eye_aspect - sentAspect_) > 1e-3f))
|
||||
UpdateEye(hp);
|
||||
if (hp.display_hz >= 24.0f) Vsync::SetRate(hp.display_hz);
|
||||
if (hp.display_hz >= 24.0f && fabsf(hp.display_hz - sentHz_) > 0.5f) UpdateRate(hp.display_hz);
|
||||
const bool worn = (hp.flags & DROIDDECK_HEADPOSE_ENABLED) != 0;
|
||||
if (worn != worn_) {
|
||||
worn_ = worn;
|
||||
@@ -642,8 +653,9 @@ private:
|
||||
|
||||
uint32_t id_ = k_unTrackedDeviceIndexInvalid;
|
||||
int32_t width_ = 1920, height_ = 1080;
|
||||
float tanHalf_ = 1.0f, eyeAspect_ = 0.889f;
|
||||
float sentTan_ = 0.0f, sentAspect_ = 0.0f;
|
||||
std::atomic<float> tanHalf_{1.0f}, eyeAspect_{0.889f}, renderScale_{1.4f};
|
||||
float sentTan_ = 0.0f, sentAspect_ = 0.0f, sentHz_ = 0.0f;
|
||||
PropertyContainerHandle_t container_ = k_ulInvalidPropertyContainer;
|
||||
public:
|
||||
void SetHands(CardboardController *left, CardboardController *right) { hands_[0] = left; hands_[1] = right; }
|
||||
|
||||
|
||||
@@ -312,21 +312,25 @@ static void VKAPI_CALL DestroyDevice(VkDevice device, const VkAllocationCallback
|
||||
* One pretend display with one mode and one plane. vrcompositor keeps a direct mode window on it even
|
||||
* when a driver-direct driver takes the frames, and waits on that window's presents, so the display
|
||||
* needs a working swapchain: its surface is a headless one (VK_EXT_headless_surface, Mesa's WSI).
|
||||
* The vblank side of VK_EXT_display_control is a 60 Hz clock. Size and rate:
|
||||
* DROIDDECK_VR_DISPLAY_MODE=WIDTHxHEIGHT@HZ (default 1920x1080@60). */
|
||||
* The vblank side of VK_EXT_display_control is a clock at the panel's rate. Size and rate:
|
||||
* DROIDDECK_VR_DISPLAY_MODE=WIDTHxHEIGHT@HZ (default 1920x1080, at the head pose file's rate, else 60). */
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdatomic.h>
|
||||
#include <time.h>
|
||||
#include "../../../app/src/main/cpp/waylandcomp/src/headpose_shm.h"
|
||||
|
||||
#define FAKE_DISPLAY ((VkDisplayKHR)(uintptr_t)0xdd0001)
|
||||
#define FAKE_MODE ((VkDisplayModeKHR)(uintptr_t)0xdd0002)
|
||||
static uint32_t g_mode_w = 1920, g_mode_h = 1080, g_mode_mhz = 60000;
|
||||
static int g_mode_fixed;
|
||||
|
||||
static void mode_init(void) {
|
||||
static int done;
|
||||
if (done++) return;
|
||||
const char *m = getenv("DROIDDECK_VR_DISPLAY_MODE");
|
||||
unsigned w, h, hz;
|
||||
if (m && sscanf(m, "%ux%u@%u", &w, &h, &hz) == 3 && w && h && hz) { g_mode_w = w; g_mode_h = h; g_mode_mhz = hz * 1000; }
|
||||
if (m && sscanf(m, "%ux%u@%u", &w, &h, &hz) == 3 && w && h && hz) { g_mode_w = w; g_mode_h = h; g_mode_mhz = hz * 1000; g_mode_fixed = 1; }
|
||||
}
|
||||
|
||||
static uint64_t now_ns(void) {
|
||||
@@ -334,7 +338,40 @@ static uint64_t now_ns(void) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec;
|
||||
}
|
||||
static uint64_t frame_ns(void) { return 1000000000000ull / g_mode_mhz; }
|
||||
static _Atomic(const struct droiddeck_headpose *) g_pose;
|
||||
static _Atomic uint64_t g_pose_tried;
|
||||
|
||||
static const struct droiddeck_headpose *pose_map(void) {
|
||||
const struct droiddeck_headpose *p = atomic_load(&g_pose);
|
||||
if (p) return p;
|
||||
uint64_t t = now_ns(), last = atomic_load(&g_pose_tried);
|
||||
if (last && t - last < 1000000000ull) return NULL;
|
||||
atomic_store(&g_pose_tried, t);
|
||||
int fd = open("/run/droiddeck/" DROIDDECK_HEADPOSE_FILE, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) return NULL;
|
||||
void *m = mmap(NULL, sizeof(struct droiddeck_headpose), PROT_READ, MAP_SHARED, fd, 0);
|
||||
close(fd);
|
||||
if (m == MAP_FAILED) return NULL;
|
||||
const struct droiddeck_headpose *expected = NULL;
|
||||
if (!atomic_compare_exchange_strong(&g_pose, &expected, (const struct droiddeck_headpose *)m)) {
|
||||
munmap(m, sizeof(struct droiddeck_headpose));
|
||||
return expected;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
static uint32_t mode_mhz(void) {
|
||||
mode_init();
|
||||
if (g_mode_fixed) return g_mode_mhz;
|
||||
const struct droiddeck_headpose *p = pose_map();
|
||||
if (!p || __atomic_load_n(&p->magic, __ATOMIC_RELAXED) != DROIDDECK_HEADPOSE_MAGIC) return g_mode_mhz;
|
||||
uint32_t bits = __atomic_load_n((const uint32_t *)&p->display_hz, __ATOMIC_RELAXED);
|
||||
float hz;
|
||||
memcpy(&hz, &bits, sizeof(hz));
|
||||
return hz >= 24.0f && hz <= 240.0f ? (uint32_t)(hz * 1000.0f + 0.5f) : g_mode_mhz;
|
||||
}
|
||||
|
||||
static uint64_t frame_ns(void) { return 1000000000000ull / mode_mhz(); }
|
||||
|
||||
static VkDisplayPropertiesKHR display_props(void) {
|
||||
mode_init();
|
||||
@@ -384,7 +421,7 @@ static VkResult VKAPI_CALL GetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice
|
||||
}
|
||||
static VkDisplayModePropertiesKHR mode_props(void) {
|
||||
mode_init();
|
||||
VkDisplayModePropertiesKHR m = {FAKE_MODE, {{g_mode_w, g_mode_h}, g_mode_mhz}};
|
||||
VkDisplayModePropertiesKHR m = {FAKE_MODE, {{g_mode_w, g_mode_h}, mode_mhz()}};
|
||||
return m;
|
||||
}
|
||||
static VkResult VKAPI_CALL GetDisplayModePropertiesKHR(VkPhysicalDevice pd, VkDisplayKHR d, uint32_t *count, VkDisplayModePropertiesKHR *p) {
|
||||
@@ -452,7 +489,7 @@ static VkResult VKAPI_CALL GetRandROutputDisplayEXT(VkPhysicalDevice pd, void *d
|
||||
*d = FAKE_DISPLAY; return VK_SUCCESS;
|
||||
}
|
||||
|
||||
/* VK_EXT_display_control on the device: a 60 Hz clock. A vblank event's fence comes back signalled
|
||||
/* VK_EXT_display_control on the device: a clock at the panel's rate. A vblank event's fence comes back signalled
|
||||
* once the next tick has passed (the caller's vblank thread waits right after registering). */
|
||||
static VkResult VKAPI_CALL DisplayPowerControlEXT(VkDevice d, VkDisplayKHR disp, const VkDisplayPowerInfoEXT *info) { return VK_SUCCESS; }
|
||||
static VkResult signalled_fence(VkDevice d, const VkAllocationCallbacks *a, VkFence *fence) {
|
||||
|
||||
Reference in new issue
Block a user