Pointer: ft-screens announces new panels to the helper

The helper now reads SteamVR's list of panels every 20 s instead of every second, so a panel
made in between (a floating window's menu, frametop.float.N.sub.K, or the Frametop keyboard
the first time it opens) couldn't be clicked with the mouse until the next read. ft-screens
now sends "overlay <key>" to @ft_pointer_helper right after it makes one, and the helper adds
it to its list at once (only frametop.* keys). An older helper ignores it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DeeJanuzandClaude Opus 5.5 committed 2026-10-03 09:28:10 -06:00
1 parent e6a931f7ee
commit 99aec84f62
2 files changed
+33 -2

No files matched your search

+15
View File
@@ -582,6 +582,17 @@ public:
for (const auto &e : entries_) keys.push_back(e.key); for (const auto &e : entries_) keys.push_back(e.key);
return keys; return keys;
} }
// A panel just made by another Frametop program (ft-screens: a floating window's menu, the
// keyboard): known at once, without waiting for the next read ("overlay <key>").
void Add(const std::string &key) {
{
std::lock_guard<std::mutex> guard(lock_);
for (const auto &e : entries_)
if (e.key == key) return;
entries_.push_back({key, key, true});
}
++generation_;
}
// Goes up by one each time the list is read, so the main loop knows to look the keys up again. // Goes up by one each time the list is read, so the main loop knows to look the keys up again.
unsigned Generation() const { return generation_; } unsigned Generation() const { return generation_; }
// Answer `to` with {"t":"overlays","list":[{"key","name","visible"}...]} after the next refresh. // Answer `to` with {"t":"overlays","list":[{"key","name","visible"}...]} after the next refresh.
@@ -1522,6 +1533,10 @@ int main() {
reply(controllerButtons.Status()); reply(controllerButtons.Status());
continue; continue;
} }
if (std::strncmp(buf, "overlay ", 8) == 0 && std::strncmp(buf + 8, "frametop.", 9) == 0) {
overlays.Add(buf + 8);
continue;
}
if (std::strncmp(buf, "overlays", 8) == 0) { if (std::strncmp(buf, "overlays", 8) == 0) {
overlays.Request(sender, senderLen); // answered from the list's thread overlays.Request(sender, senderLen); // answered from the list's thread
continue; continue;
+18 -2
View File
@@ -966,6 +966,19 @@ void SendFloat(const std::string &msg) {
std::printf("to ft-floatd: %s\n", msg.c_str()); std::printf("to ft-floatd: %s\n", msg.c_str());
} }
// A new panel: the pointer helper reads SteamVR's list of panels only every 20 s, so it's told
// at once ("overlay <key>"), or the mouse couldn't click a menu until then.
void AnnounceOverlay(const char *key) {
static const int fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
sockaddr_un addr{};
addr.sun_family = AF_UNIX;
const char name[] = "ft_pointer_helper";
std::memcpy(addr.sun_path + 1, name, sizeof name - 1);
const std::string msg = std::string("overlay ") + key;
sendto(fd, msg.data(), msg.size(), MSG_DONTWAIT, reinterpret_cast<sockaddr *>(&addr),
socklen_t(offsetof(sockaddr_un, sun_path) + 1 + sizeof name - 1));
}
// Where a device's ray meets the screen's plane, in the screen's x (right) and y (up), // Where a device's ray meets the screen's plane, in the screen's x (right) and y (up),
// metres from its centre. // metres from its centre.
bool RayOnPlane(const Mat &p, const Mat &d, double *x, double *y) { bool RayOnPlane(const Mat &p, const Mat &d, double *x, double *y) {
@@ -1313,6 +1326,7 @@ void SetSub(Screen &s, int index, int k, int x, int y, int w, int h) {
vr::VROverlay()->SetOverlayFlag(sub.overlay, vr::VROverlayFlags_SendVRDiscreteScrollEvents, true); vr::VROverlay()->SetOverlayFlag(sub.overlay, vr::VROverlayFlags_SendVRDiscreteScrollEvents, true);
vr::VROverlay()->SetOverlayFlag(sub.overlay, vr::VROverlayFlags_MakeOverlaysInteractiveIfVisible, s.lasers); vr::VROverlay()->SetOverlayFlag(sub.overlay, vr::VROverlayFlags_MakeOverlaysInteractiveIfVisible, s.lasers);
vr::VROverlay()->SetOverlaySortOrder(sub.overlay, 5); vr::VROverlay()->SetOverlaySortOrder(sub.overlay, 5);
AnnounceOverlay(key);
it = s.subs.emplace(k, sub).first; it = s.subs.emplace(k, sub).first;
if (s.shown) { if (s.shown) {
auto imp = g_imports.find(s.shown); auto imp = g_imports.find(s.shown);
@@ -1573,7 +1587,7 @@ void UpdateSteamInFront() {
g_keyboardAside = true; g_keyboardAside = true;
} else if (!front && g_keyboardAside) { } else if (!front && g_keyboardAside) {
g_keyboardAside = false; g_keyboardAside = false;
keyboard::Show(g_asidePose); if (keyboard::Show(g_asidePose)) AnnounceOverlay("frametop.keyboard");
} }
} }
@@ -2020,7 +2034,9 @@ bool ft_vr_keyboard_show(int index) {
std::printf("keyboard: waiting for Steam to close\n"); std::printf("keyboard: waiting for Steam to close\n");
return true; return true;
} }
return keyboard::Show(FacingPose(at, head)); if (!keyboard::Show(FacingPose(at, head))) return false;
AnnounceOverlay("frametop.keyboard");
return true;
} }
void ft_vr_keyboard_hide(void) { void ft_vr_keyboard_hide(void) {