diff --git a/screens/handcut.cpp b/screens/handcut.cpp index a64218c..126d322 100644 --- a/screens/handcut.cpp +++ b/screens/handcut.cpp @@ -1,6 +1,8 @@ // Hand cutouts (see handcut.h). #include "handcut.h" +#include "../hands/include/fh_hands.h" + #include #include #include @@ -23,10 +25,8 @@ namespace handcut { namespace { -// The hands file (frame-hands/include/fh_hands.h). -constexpr char kMagic[8] = {'F', 'H', 'H', 'A', 'N', 'D', 'S', '1'}; -constexpr size_t kHeader = 64, kHand = 272, kCapsule = 32, kMaxHands = 2, kMaxCapsules = 64; -constexpr size_t kFileSize = kHeader + kMaxHands * kHand + kMaxCapsules * kCapsule; +// The hands file ft-hands publishes (hands/include/fh_hands.h). +constexpr uint32_t kMaxHands = FH_HANDS_MAX_HANDS, kMaxCapsules = FH_HANDS_MAX_CAPSULES; constexpr int64_t kStaleNs = 300'000'000; // hands older than this are gone constexpr int64_t kHistoryNs = 1'000'000'000; constexpr double kNear = 0.12; // metres: nothing closer to an eye than this is cut @@ -59,53 +59,44 @@ bool Hands::Read() { const int64_t now = MonoNs(); if (now - lastOpenTry_ < 1'000'000'000) return false; lastOpenTry_ = now; - const char *run = std::getenv("XDG_RUNTIME_DIR"); - const std::string path = std::string(run ? run : "/run/user/" + std::to_string(getuid())) + "/frame-hands/hands"; + const std::string path = "/run/user/" + std::to_string(getuid()) + "/frametop/hands"; fd_ = open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW); if (fd_ < 0) return false; struct stat st; - if (fstat(fd_, &st) < 0 || st.st_uid != getuid() || size_t(st.st_size) < kFileSize) { + if (fstat(fd_, &st) < 0 || st.st_uid != getuid() || size_t(st.st_size) < sizeof(fh_hands_t)) { close(fd_), fd_ = -1; return false; } - void *m = mmap(nullptr, kFileSize, PROT_READ, MAP_SHARED, fd_, 0); + void *m = mmap(nullptr, sizeof(fh_hands_t), PROT_READ, MAP_SHARED, fd_, 0); if (m == MAP_FAILED) { close(fd_), fd_ = -1; return false; } map_ = m; } - const auto *p = static_cast(map_); - auto u64 = [&](size_t off) { uint64_t v; std::memcpy(&v, const_cast(p) + off, 8); return v; }; - const uint64_t s1 = __atomic_load_n(reinterpret_cast(const_cast(p) + 16), __ATOMIC_ACQUIRE); + const auto *file = static_cast(map_); + const auto *seq = const_cast(&file->seq); + const uint64_t s1 = __atomic_load_n(seq, __ATOMIC_ACQUIRE); if ((s1 & 1) || s1 == seq_) return false; - uint8_t copy[kFileSize]; - std::memcpy(copy, const_cast(p), kFileSize); + fh_hands_t copy; + std::memcpy(static_cast(©), map_, sizeof copy); __atomic_thread_fence(__ATOMIC_ACQUIRE); - if (u64(16) != s1 || std::memcmp(copy, kMagic, 8) != 0) return false; + if (__atomic_load_n(seq, __ATOMIC_RELAXED) != s1 || std::memcmp(copy.magic, FH_HANDS_MAGIC, 8) != 0) return false; seq_ = s1; - uint64_t capture, publish; - uint32_t nhands, ncaps; - std::memcpy(&capture, copy + 24, 8); - std::memcpy(&publish, copy + 32, 8); - std::memcpy(&nhands, copy + 40, 4); - std::memcpy(&ncaps, copy + 44, 4); - captureNs_ = int64_t(capture), publishNs_ = int64_t(publish); + const uint32_t nhands = std::min(copy.nhands, kMaxHands), ncaps = std::min(copy.ncapsules, kMaxCapsules); + captureNs_ = int64_t(copy.capture_ns), publishNs_ = int64_t(copy.publish_ns); const Mat head = HeadAt(captureNs_); // each hand's palm in the room, and its velocity from the last time it was seen ids_.clear(); std::vector owners; // the hand each capsule belongs to, in file order - for (uint32_t k = 0; k < std::min(nhands, kMaxHands); ++k) { - const uint8_t *h = copy + kHeader + k * kHand; - uint32_t id, n; - float pts[21][3]; - std::memcpy(&id, h, 4); - std::memcpy(pts, h + 16, sizeof pts); - std::memcpy(&n, h + 268, 4); + for (uint32_t k = 0; k < nhands; ++k) { + const fh_hand_t &h = copy.hands[k]; + const uint32_t id = h.id; + const auto &pts = h.pts; const int idx = int(ids_.size()); ids_.push_back(id); - owners.insert(owners.end(), std::min(n, kMaxCapsules), idx); + owners.insert(owners.end(), std::min(h.ncapsules, kMaxCapsules), idx); double palm[3] = {0, 0, 0}; bool ok = true; for (int j : {0, 5, 9, 13, 17}) { @@ -137,19 +128,18 @@ bool Hands::Read() { } for (auto it = motion_.begin(); it != motion_.end();) it = captureNs_ - it->second.ns > kStaleNs ? motion_.erase(it) : std::next(it); - if (owners.size() != std::min(ncaps, kMaxCapsules)) owners.assign(std::min(ncaps, kMaxCapsules), -1); + if (owners.size() != ncaps) owners.assign(ncaps, -1); base_.clear(), owner_.clear(); - for (uint32_t k = 0; k < std::min(ncaps, kMaxCapsules); ++k) { - float f[8]; - std::memcpy(f, copy + kHeader + kMaxHands * kHand + k * kCapsule, sizeof f); + for (uint32_t k = 0; k < ncaps; ++k) { + const fh_capsule_t &f = copy.capsules[k]; bool ok = true; - for (float v : f) ok = ok && std::isfinite(v) && std::fabs(v) < 10; - if (!ok || f[6] <= 0 || f[7] <= 0) continue; + for (float v : {f.a[0], f.a[1], f.a[2], f.b[0], f.b[1], f.b[2], f.ra, f.rb}) ok = ok && std::isfinite(v) && std::fabs(v) < 10; + if (!ok || f.ra <= 0 || f.rb <= 0) continue; Capsule c; - Apply(head, f, c.a); - Apply(head, f + 3, c.b); - c.ra = f[6], c.rb = f[7]; + Apply(head, f.a, c.a); + Apply(head, f.b, c.b); + c.ra = f.ra, c.rb = f.rb; base_.push_back(c); owner_.push_back(owners[k]); } diff --git a/screens/handcut.h b/screens/handcut.h index 15dc609..59288ab 100644 --- a/screens/handcut.h +++ b/screens/handcut.h @@ -1,8 +1,8 @@ // Hand cutouts: where a tracked hand is between an eye and a screen, that eye sees the // room (Room View) through the screen instead of the screen drawn over the hand. // -// frame-hands' tracker (a separate project, ~/Desktop/Projects/frame-hands) publishes -// the hands it sees with the headset's cameras to $XDG_RUNTIME_DIR/frame-hands/hands: +// ft-hands (hands/) publishes the hands it sees with the headset's cameras to +// /run/user/UID/frametop/hands (hands/include/fh_hands.h): // capsules (finger bones, palm, forearm) in the head frame at capture time. Hands turns // them into the room with the head pose at that time. Project() finds where each eye // sees them on a panel, and Renderer draws the panel's client buffer into a side-by-side diff --git a/screens/handtest.cpp b/screens/handtest.cpp index 0b4692d..6b15091 100644 --- a/screens/handtest.cpp +++ b/screens/handtest.cpp @@ -1,10 +1,10 @@ // ft-handtest: try the hand cutouts without restarting the desktop. Shows a test panel -// (a light grid) in front of you as its own overlay; where frame-hands tracks your hands +// (a light grid) in front of you as its own overlay; where ft-hands tracks your hands // in front of it, each eye sees through it, like ft-screens' screens with cutouts. // // ft-handtest [--distance m] [--width m] [--seconds s] // -// Needs frame-hands' tracker running (it publishes $XDG_RUNTIME_DIR/frame-hands/hands). +// Needs hand tracking running (hands/run.sh; ft-hands publishes /run/user/UID/frametop/hands). // Build: screens/build.sh (build/ft-handtest), run in the dev container. #include "handcut.h" diff --git a/screens/vr.cpp b/screens/vr.cpp index b146bf7..17c069d 100644 --- a/screens/vr.cpp +++ b/screens/vr.cpp @@ -40,7 +40,7 @@ // own; also for flatscreen games, which aren't scene apps). // - during a VR game the screens hide unless the dashboard is open (g_inGames, default), // or stay visible over it; the hotkey still shows them. -// - hand cutouts (handcut.cpp): where frame-hands tracks a hand between an eye and a +// - hand cutouts (handcut.cpp): where ft-hands (hands/) tracks a hand between an eye and a // screen, that eye sees through the screen (to Room View). Only then is the screen // drawn by us, into a side-by-side buffer (one half per eye); otherwise its client // buffer is shown as is.