Arrange the desktop's outputs the way the screens are around you

KWin's output positions now follow where the Frametop screens are in the room
instead of their numbers: a screen you see to the left of another is to its
left in Plasma, so the pointer and dragged windows cross straight to it.
Screens one above the other stack, and wrist-pinned screens go last.
ft-layout scale works this out from ft-screens' head pose and screen poses,
and runs after arranging, capturing, or pinning; ft-screens runs it half a
second after a screen is let go. With no head pose (headset off) KWin's
order is kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DeeJanuzandClaude Opus 5.5 committed 2026-09-27 22:51:07 -06:00
1 parent c43418085d
commit e84ac22313
3 files changed
+104 -10

No files matched your search

+34
View File
@@ -41,7 +41,12 @@
#include <openvr.h>
#include <fcntl.h>
#include <linux/input-event-codes.h>
#include <limits.h>
#include <spawn.h>
extern char **environ; // for posix_spawn
#include <algorithm>
#include <chrono>
@@ -854,6 +859,33 @@ void EndDrag(Screen &s) {
ApplyAlpha(s);
}
// KWin's outputs follow where the screens are, so the pointer and dragged windows cross
// to the screen you see next to this one: `ft-layout scale` runs once a move has settled.
long g_arrangeAt = -1; // g_tick to run it at, -1 = not pending
void ArrangeDesktopSoon() { g_arrangeAt = g_tick + 45; } // about half a second
void UpdateArrange() {
if (g_arrangeAt < 0 || g_tick < g_arrangeAt) return;
g_arrangeAt = -1;
char exe[PATH_MAX];
if (!realpath("/proc/self/exe", exe)) return;
std::string layout(exe); // <repo>/screens/build/ft-screens -> <repo>/layout/ft-layout
for (int up = 0; up < 3 && layout.rfind('/') != std::string::npos; ++up) layout.resize(layout.rfind('/'));
layout += "/layout/ft-layout";
posix_spawn_file_actions_t io;
posix_spawn_file_actions_init(&io);
posix_spawn_file_actions_addopen(&io, 0, "/dev/null", O_RDONLY, 0);
posix_spawn_file_actions_addopen(&io, 1, "/tmp/frametop-layout.log", O_WRONLY | O_CREAT | O_APPEND, 0644);
posix_spawn_file_actions_adddup2(&io, 1, 2);
char scale[] = "scale";
char *argv[] = {layout.data(), scale, nullptr};
pid_t pid; // reaped by the compositor's SIGCHLD handler
if (posix_spawn(&pid, layout.c_str(), &io, nullptr, argv, environ) != 0)
std::printf("can't run %s\n", layout.c_str());
posix_spawn_file_actions_destroy(&io);
}
// Let go: pin to the armed wrist, as the screen is now.
void FinishDrag(Screen &s, int index) {
const bool moved = s.drag == Drag::Move;
@@ -861,6 +893,7 @@ void FinishDrag(Screen &s, int index) {
EndDrag(s);
Mat c, p;
if (!moved) return;
ArrangeDesktopSoon();
if (target != kNone && DevicePose(target, &c) && ScreenPose(s, &p)) {
Pin(s, target, Mul(Inverse(c), p));
std::printf("screen %d: pinned to the %s controller\n", index + 1, HandName(target));
@@ -1228,6 +1261,7 @@ void ft_vr_poll(void (*handle)(const struct ft_event *, void *), void *data) {
}
++g_tick;
UpdateGame();
UpdateArrange();
UpdateVisibility();
UpdateLasers();
UpdateControls();