Profiles: named layouts that open apps

A profile is a named layout plus the screens it hides and its apps' windows
(docs/profiles.md). ft-layout save captures them (ft-floatd's "windows",
after the KWin script reports every window as it is now); use opens them:
the screens move, open windows of each app go to their places (on a screen,
maximized or not, or floating), and missing apps start, once and then again
for each window still missing 3 s after the first. Nothing closes.

The desktop starts in FT_PROFILE or default_profile (ft-layout start, from
the session script). Each profile gets a launcher entry (Frametop: NAME, in
SteamVR's Launch a program list) that switches to it or starts the desktop
in it. The relay's profile:NAME action and Input Settings' "Open profile"
entries put one on a key, mouse button, or controller button. Display
Settings' Layout page becomes Layout & profiles: Save as profile, Open
profile, the profile's apps, and Start in profile. Plasma's own session
restore is off in the Frametop session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DeeJanuzandClaude Opus 5.5 committed 2026-09-30 22:32:10 -06:00
1 parent 9d91ecbcaa
commit 858dbe1562
11 files changed
+619 -72

No files matched your search

+6 -1
View File
@@ -48,7 +48,7 @@ function info(w) {
frame: rect(w.frameGeometry), client: rect(w.clientGeometry), popup: w.popupWindow,
transient: w.transient, parent: w.transientFor ? String(w.transientFor.internalId) : "",
normal: w.normalWindow, dialog: w.dialog, fullScreen: w.fullScreen, minimized: w.minimized,
onAllDesktops: w.onAllDesktops
onAllDesktops: w.onAllDesktops, maximized: w.maximizeMode === 3
};
}
@@ -172,6 +172,7 @@ function run(c) {
workspace.sendClientToScreen(w, o);
w.frameGeometry = {x: c.x, y: c.y, width: c.w, height: c.h};
if (c.onAllDesktops !== undefined) w.onAllDesktops = c.onAllDesktops;
if (c.maximized) w.setMaximize(true, true);
break;
}
case "geometry":
@@ -189,6 +190,10 @@ function run(c) {
case "info":
if (w) report("window", w);
break;
case "report-all": // a profile's capture: every window as it is now, then a marker
workspace.windowList().forEach(w => report("window", w));
send({ev: "reported", token: c.token});
break;
case "request-active": // ft-float float|dock active
requestFloat(workspace.activeWindow);
break;