mirror of
https://github.com/DeeJanuz/frametop.git
synced 2026-10-06 08:00:09 +02:00
Float key in the input relay, docking everything, and the plan for profiles
The input relay owns the float key now: float_toggle (Meta+Shift+F unless the rules have their own key combinations) floats the window under the pointer, or the active one over the wallpaper, and docks it if it floats. dock_all puts every floating window back. Both are mappable to mouse and controller buttons, and work without pointer mode. The KWin script no longer registers a shortcut, and ft-floatd drops the old one. Key combinations move to Input Settings' Keyboard page. docs/floating-windows.md records the decisions from 2026-09-30 (the title bar button, Launch as Standalone, profiles), and docs/profiles.md plans profiles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e16b7588e2
commit
1e83f96b0f
9 files changed
+220
-69
No files matched your search
+27
-10
@@ -10,7 +10,9 @@ Built so far (2026-09-29; the KWin side tested on the headless test desktop, `sc
|
||||
- The session adds `FLOAT_SLOTS` spares and starts ft-floatd from the desktop's autostart; ft-layout leaves the spares alone.
|
||||
- The 3D mouse's drag lock crosses onto other Frametop panels (not while carrying one).
|
||||
|
||||
Not built yet: phase 2 (the ghost, tear-off by dragging, push-flush docking), phase 3 (launching floating, the Frametop Apps entry and picker, remembered placement), and phase 4. Frametop Apps (decision 10) needs a per-screen hide, which ft-screens doesn't have yet: its hide and show are for all screens.
|
||||
Not built yet: phase 2 (the ghost, tear-off by dragging, push-flush docking), phase 3 (launching floating, Launch as Standalone, remembered placement), and phase 4.
|
||||
|
||||
Build order from 2026-09-30, each merged into `experimental` when done: (1) the float key and docking everything, (2) the title bar button, (3) phase 3, (4) hiding screens one at a time, (5) profiles (`docs/profiles.md`).
|
||||
|
||||
The goal is to let any desktop app float in VR in a panel of its own, like SteamVR's floating windows, while it stays part of the Frametop desktop. That means drag and drop, the clipboard, and focus keep working between floating windows and the screens.
|
||||
|
||||
@@ -20,7 +22,7 @@ The goal is to let any desktop app float in VR in a panel of its own, like Steam
|
||||
|
||||
## Decisions
|
||||
|
||||
Settled with the user on 2026-09-29. The sections below follow them.
|
||||
Settled with the user on 2026-09-29 (1 to 21) and 2026-09-30 (22 to 27). The sections below follow them.
|
||||
|
||||
| # | Question | Decision |
|
||||
|---|---|---|
|
||||
@@ -30,10 +32,10 @@ Settled with the user on 2026-09-29. The sections below follow them.
|
||||
| 4 | Docking by dragging | Push the window flush against a screen (within about 10 cm), with the landing spot highlighted, and let go |
|
||||
| 5 | Visibility | Floating windows follow the same rules as the screens: the hide hotkey, the visibility modes, and the games rule |
|
||||
| 6 | Windows a floating app opens | They float too |
|
||||
| 7 | Launching floating from the headset | One "Frametop Apps" launcher entry with a picker |
|
||||
| 7 | Launching floating from the headset | ~~One "Frametop Apps" launcher entry with a picker~~ Replaced by 26 and profiles (27) |
|
||||
| 8 | Build order | The catcher first, as a fix that stands on its own; `pointer-ignore` and `layouts-headpin` merged before phase 1; the hand cutouts stay out |
|
||||
| 9 | Show Desktop (Meta+D) | Floating windows stay |
|
||||
| 10 | Frametop Apps and visibility | The entry starts the desktop with each screen hidden on its own (the existing per-screen hide), so only floating windows show. No special mode |
|
||||
| 10 | Frametop Apps and visibility | ~~The entry starts the desktop with each screen hidden on its own, so only floating windows show~~ Replaced: a profile can hide screens (27) |
|
||||
| 11 | Window frame | KWin's title bar and border stay. Frametop's bar, close, and "back to desktop" are extras |
|
||||
| 12 | Resizing | The window's own edges and Frametop's corner tab both change the size in pixels at the same density; the output follows |
|
||||
| 13 | Margin | 300 px on each side, configurable |
|
||||
@@ -44,7 +46,13 @@ Settled with the user on 2026-09-29. The sections below follow them.
|
||||
| 18 | Bigger text | A scale for each window (KWin's output scale): Meta+scroll over the window, or +/- on its bar. Remembered for each app |
|
||||
| 19 | Switching to a window you can't see | It's focused, and a glow at the edge of your view points to it. Moving it in front of you is a setting |
|
||||
| 20 | Full screen | The window fills its own panel. The margin drops to zero while it's full screen, and the panel keeps its size and place |
|
||||
| 21 | Named layouts | They cover the screens only. Floating windows use the placement remembered for each app |
|
||||
| 21 | Named layouts | ~~They cover the screens only~~ Replaced by profiles (27). Outside a profile, floating windows use the placement remembered for each app |
|
||||
| 22 | The float key | One toggle: it floats a window, or docks it if it already floats. The input relay owns it (`float_toggle`), Meta+Shift+F by default, rebindable in Frametop Input Settings and mappable to mouse and controller buttons. KWin has no shortcut of its own for it, so one press can't fire twice |
|
||||
| 23 | Which window the key acts on | The window under the desktop's pointer; the active window if there's none there (the wallpaper, the taskbar) |
|
||||
| 24 | Docking everything | A `dock_all` action, with no default binding |
|
||||
| 25 | A button on every window | A float button left of Close in the title bar, from Frametop's own QML window decoration, made to look like Breeze. It shows a dock icon on floating windows. Apps that draw their own title bar (Chromium, Electron, GTK) use the key |
|
||||
| 26 | Launching one app floating | "Launch as Standalone" in the right-click menu of every app in the Application Launcher and the taskbar, from copies of the apps' desktop files that only the Frametop desktop reads. It replaces the Frametop Apps entry (7, 10) |
|
||||
| 27 | Profiles | Named layouts become profiles: the screens' places, which screens show, and the apps and their windows, floating or not. See `docs/profiles.md` |
|
||||
|
||||
Also assumed: floating windows get the wrist pin, the head pin, and pass-through (`pointer-ignore`) like screens. Every gesture works with the controllers as well as the 3D mouse. A window launched floating uses the primary screen's density. VNC shows only the primary screen, as now. Anything that restarts the live desktop waits for the user's OK.
|
||||
|
||||
@@ -121,13 +129,22 @@ Program names stay within 15 characters (`ft-floatd`). Overlay keys are `frameto
|
||||
- **Dragging.** Carry the floating window, by its title bar or its bar, until the spot you're pointing at is on a screen. Then push it flush with the screen, within about 10 cm of its surface: scroll away with the mouse, or move the controller forward. The screen shows where the window will land, and letting go docks it there at its current size in pixels, shrunk to fit if the screen is smaller. A carried panel keeps its distance, so moving a floating window in front of a screen never docks it by accident.
|
||||
- Docking disables the output and removes the panel.
|
||||
|
||||
## Getting at it: the float key and the title bar button
|
||||
|
||||
Settled on 2026-09-30 (decisions 22 to 25).
|
||||
|
||||
- **The float key.** The input relay owns it: the action `float_toggle`, bound to Meta+Shift+F unless the rules file says otherwise (a rules file with no `key_bindings` gets that default; one with its own list, even an empty one, doesn't). It can be rebound or removed in Frametop Input Settings, and mapped to a mouse button or a Frame controller button like any other action. The relay takes the combination before it reaches the desktop and sends `float pointer` to ft-floatd, which asks the script for the window under KWin's pointer (`workspace.cursorPos`, top of `workspace.stackingOrder`, popups and dialogs counting as their parent). With none there, the wallpaper or the taskbar, it's the active window. KWin's pointer is where the 3D mouse or a laser last was on a Frametop panel. A window that floats docks; any other floats. The KWin script no longer registers a shortcut of its own, so one press can't float a window and dock it again.
|
||||
- **Docking everything.** `dock_all` (no default binding) sends `dock all`, which docks every floating window where it came from.
|
||||
- **The title bar button.** Breeze can't take a button of its own, and a C++ fork of it would have to match SteamOS's exact KDecoration build (Plasma 6.3 replaces KDecoration2 with KDecoration3). So the Frametop desktop gets its own window decoration, written in QML for KWin's Aurorae engine, which loads it without compiling (`decoration/`, installed to `~/.local/share/kwin/decorations/kwin4_decoration_qml_frametop`, chosen in the session's `kwinrc` only, so Desktop Mode keeps Breeze). It's drawn to look like Breeze, with a float button left of Close. The button calls `requestToggleKeepBelow()`, the one window request a decoration can make that has no visible effect here, and the KWin script reads the change: keep-below set on a window on the screens floats it, cleared on a floating window docks it. The script keeps keep-below set on every floating window, however it was floated, so the button shows its dock icon there. A window alone on its own output loses nothing by being kept below (only the wallpaper is under it). If the window can't float (every spare is in use), the script clears the flag again. Keep Below Others in a window's menu does the same as the button.
|
||||
- **Apps that draw their own title bar** (Chromium and Electron apps, GTK apps) never show KWin's decoration, so they don't get the button. They use the key, or the window menu (Alt+F3).
|
||||
|
||||
## Launching an app floating
|
||||
|
||||
- **In the desktop.** Use "Float in VR" in any window's menu, or press Meta+Shift+F for the active window.
|
||||
- **In the desktop.** Use "Float in VR" in any window's menu, its title bar button, or the float key.
|
||||
- **From the menu.** Right-click an app in the Application Launcher, or in the taskbar (where it starts another window of that app), and pick "Launch as Standalone" (decision 26). The launcher has no way to add an entry to every app's menu, but its menu shows each app's own desktop actions. So the Frametop desktop reads copies of the apps' desktop files with one more action added. They're written to `~/.local/share/frametop/apps/applications` from every desktop file in `XDG_DATA_DIRS`: by the session script before Plasma starts, and by ft-floatd whenever an app is installed, changed, or removed. The session puts `~/.local/share/frametop/apps` first in `XDG_DATA_DIRS`. Plasma's app cache is keyed by those directories, so Desktop Mode never sees the copies. Desktop files in `~/.local/share/applications` come before every data dir, so an app you've customized there keeps your copy and has no Launch as Standalone. The action runs `ft-float launch <desktop file name>`.
|
||||
- **From a command.** `ft-float run <command>` and `ft-float launch <app.desktop>` start an app and float its first window. ft-floatd records the process it started, and the script matches new windows by PID, including child processes. Some single-instance apps (Firefox, D-Bus-activated apps) open the window from a process that was already running. Those are matched by desktop file name within a few seconds, or by `XDG_ACTIVATION_TOKEN` where the app honors it.
|
||||
- **From the headset without the desktop open.** One new launcher entry, "Frametop Apps". It starts the Frametop session with each screen hidden on its own (the per-screen hide that already exists), and opens an app picker as a floating window. Picking an app launches it floating. The hide hotkey and visibility modes still apply to everything, and a screen comes back with one click. Everything runs in one session, so dragging between a standalone app and a desktop app works. If the desktop is already running, the entry just opens the picker.
|
||||
- **The picker.** Either KRunner, floated, or a small Kirigami app like the settings apps, with a grid of apps and their icons.
|
||||
- **Remembered placement.** Each app's last floating pose, size, and scale, keyed by desktop file name. Named layouts don't include floating windows.
|
||||
- **From the headset with the desktop off.** A profile's launcher entry starts the desktop in that profile, and a profile can hide every screen and hold only floating apps (`docs/profiles.md`). There's no separate Frametop Apps entry or picker.
|
||||
- **Remembered placement.** Each app's last floating pose, size, and scale, keyed by desktop file name. A profile's own placement wins when the profile opens the app.
|
||||
|
||||
## Drag and drop between panels
|
||||
|
||||
@@ -196,7 +213,7 @@ The ghost and tear-off, and the dock highlight with push-flush docking.
|
||||
|
||||
### Phase 3: launch floating
|
||||
|
||||
`ft-float run` and `ft-float launch`, window matching, new windows of floating apps, the Frametop Apps launcher entry, the picker, remembered placement, and the notification when every spare is in use.
|
||||
`ft-float run` and `ft-float launch`, window matching, new windows of floating apps, Launch as Standalone, remembered placement, and the notification when every spare is in use.
|
||||
|
||||
### Phase 4: polish
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# Profiles (plan)
|
||||
|
||||
Status: design settled with the user on 2026-09-30; not built yet. Profiles come last in the build order in `docs/floating-windows.md`, after launching apps floating (phase 3 there) and hiding screens one at a time.
|
||||
|
||||
A profile is a named layout that also opens apps. It holds:
|
||||
|
||||
- where each screen goes, with its size in metres, curve, roll, and pin (what a named layout holds today);
|
||||
- which screens show and which are hidden;
|
||||
- the apps, one entry per window: on a screen at a place and size, or floating at a pose, size, and scale.
|
||||
|
||||
So a "Work" profile can put three screens around you with a browser, two terminals, and an editor on them, and a "Couch" profile can hide every screen and float one video player in front of you.
|
||||
|
||||
## Decisions
|
||||
|
||||
| # | Question | Decision |
|
||||
|---|---|---|
|
||||
| 1 | Profiles and named layouts | One list. Named layouts grow into profiles; a layout saved before profiles existed is a profile with no apps and every screen shown |
|
||||
| 2 | Making one | Capture what's open: the screens and every app's windows. Display Settings lists a profile's apps, so one can be removed. No editor beyond that |
|
||||
| 3 | Apps with several windows | One entry per window. The app is launched once; when its first window shows up, it's launched again for each window still missing. A browser that restores its own windows gets launched once, a terminal twice |
|
||||
| 4 | What's recorded of an app | Its desktop file name, or its command line if it has none. Not what it had open: tabs, files, and folders are left to the app's own restore |
|
||||
| 5 | Switching while apps are open | Additive: launch what's missing, move the windows that match into place, leave the rest alone. Nothing is ever closed |
|
||||
| 6 | Saving changes | Only on an explicit save. Moving things after switching doesn't change the profile |
|
||||
| 7 | Starting one | Four ways: a default profile when the desktop starts, Display Settings, a launcher entry for each profile, and a mappable action |
|
||||
| 8 | Plasma's session restore | Off in the Frametop session, so a profile is the only thing that reopens apps |
|
||||
| 9 | Screen count and resolution | Not part of a profile. They're global, because changing them restarts the desktop, which closes every window |
|
||||
|
||||
## Where profiles live
|
||||
|
||||
`~/.config/frametop-layout.json` keeps its `layouts` as they are (`{"Work": [screen places]}`), so older copies of ft-layout (the `main` checkout) still read it. What a profile adds goes in a parallel `profiles` map under the same names:
|
||||
|
||||
```
|
||||
"layouts": {"Work": [{"pos": ..., "face": ..., "roll": ..., "metres": ..., "curve": ..., "pin": ...}, ...]},
|
||||
"profiles": {"Work": {"hidden": [3],
|
||||
"windows": [
|
||||
{"app": "org.kde.konsole", "screen": 2, "rect": [40, 60, 1200, 800], "maximized": false},
|
||||
{"app": "com.brave.Browser", "screen": 1, "maximized": true},
|
||||
{"cmd": ["/opt/tool/run"], "class": "tool", "screen": 1, "rect": [...]},
|
||||
{"app": "org.kde.dolphin", "float": {"pos": [...], "face": [...], "roll": 0,
|
||||
"pixels": [1400, 900], "scale": 1.2, "pin": ...}}]}},
|
||||
"default_profile": "Work"
|
||||
```
|
||||
|
||||
- `screen` is 1-based, as everywhere in Frametop. `rect` is the window's frame in KWin's logical units, relative to its screen's output, so it survives the screens being arranged differently.
|
||||
- A floating window's place is relative to your head when the profile is applied, the same way the screens' places are (position, and the direction you face, yaw only).
|
||||
- Renaming or deleting a layout renames or deletes its profile entry with it.
|
||||
|
||||
## How it works
|
||||
|
||||
- **Capture** (`ft-layout save NAME`, and Save current arrangement… in Display Settings). ft-layout captures the screens as today, then asks ft-floatd for the windows (`windows` on @frametop_float). ft-floatd answers from the KWin script's table: every normal window with its desktop file name, output, frame, and maximized state, and for floating windows their panel's pose from ft-screens, their size in pixels, and their scale. Windows with no desktop file name are kept by their process's command line (`/proc/<pid>/cmdline`) and window class. Windows of Plasma itself, the Frametop settings apps, and dialogs aren't recorded.
|
||||
- **Apply** (`ft-layout use NAME`). ft-layout arranges the screens as today, hides and shows them (the per-screen hide in ft-screens), then hands the profile's windows to ft-floatd (`profile NAME`). ft-floatd goes through the entries app by app. It claims windows of that app already open (oldest first, each claimed once), and moves each to its entry's place: onto its screen at its rect (or maximized), or floating at its pose. For the entries left over, it launches the app (`ft-float launch`, the same path as Launch as Standalone), places windows as they show up, and launches again for each one still missing once the first has appeared. It gives up on an app after 30 seconds and notifies.
|
||||
- **Default at start.** `default_profile` replaces today's "arrange when the desktop starts": the session's autostart runs `ft-layout start`, which applies the default profile (screens and apps), or just arranges the screens if there's none. Plasma's session restore is turned off in the session (`ksmserverrc`: `loginMode=emptySession`).
|
||||
- **Launcher entries.** Each profile gets `~/.local/share/applications/frametop-profile-<name>.desktop` ("Frametop: Work"), written when it's saved and removed when it's deleted. They show in SteamVR's Launch a program list, the Application Launcher, and KRunner. Running one (`ft-layout open NAME`) switches to that profile if the desktop runs, or starts the desktop with `FT_PROFILE` set, which overrides `default_profile` for that start.
|
||||
- **The action.** `profile:NAME` in the input relay, for key combinations, mouse buttons, and controller buttons. Input Settings lists one entry per profile.
|
||||
- **Display Settings.** The Layout page becomes Profiles: the list (switch, rename, delete, save the current state over one), each profile's apps with a remove button, which screens it hides, and which profile the desktop starts with.
|
||||
|
||||
## Open for when it's built
|
||||
|
||||
- How long to wait for an app's first window before launching it again (slow apps would open twice). Start at waiting for the first window, up to 30 seconds, then 3 seconds more for windows it restores itself.
|
||||
- Apps that are D-Bus activated or single-instance open their window from a process that was already running; matching them is phase 3's job (`docs/floating-windows.md`, Launching an app floating).
|
||||
Reference in new issue
Block a user