From 9c0facd0c5f556dcbe06dc78a5affe69717ae828 Mon Sep 17 00:00:00 2001 From: DeeJanuz <45082401+DeeJanuz@users.noreply.github.com> Date: Thu, 8 Oct 2026 09:22:14 -0600 Subject: [PATCH] Quick reset opens the profile in use, as Open profile does ft-layout reset reopens the profile in use (the custom arrangement with an active profile) with use NAME, so its screens, hidden screens, remote displays and apps all go back as the profile has them. Without one it runs apply, as before. Meta+Shift+R and the Reset Screen Layout entry (ft-layout-reset), the input relay's layout_reset action (a mapped mouse or controller button), and the reset button on a screen's bar now run it. Display Settings' Arrange now still only arranges. Before, every quick reset ran apply, which put the desktop screens back but left the profile's hidden screens, remote displays and apps alone. Co-Authored-By: Claude Opus 5.5 --- docs/profiles.md | 1 + docs/reference.md | 4 ++-- input/input-relay.py | 6 +++--- layout/ft-layout-reset | 2 +- layout/ft_layout.py | 7 +++++++ screens/vr.cpp | 10 +++++----- 6 files changed, 19 insertions(+), 11 deletions(-) diff --git a/docs/profiles.md b/docs/profiles.md index 4ada28b..b9438e3 100644 --- a/docs/profiles.md +++ b/docs/profiles.md @@ -51,5 +51,6 @@ So a "Work" profile can put three screens around you with a browser, two termina - **Apply** (`ft-layout use NAME`, Open profile in Display Settings). ft-layout makes the profile's hidden screens the screens' own setting, arranges the screens (which hides and shows them: ft-screens' `conceal` and `reveal`), then has ft-floatd open the apps (`profile NAME`; ft-floatd reads the windows from the layout file). If the screens can't be arranged, for example with the headset off and no head pose, the apps still open: the screens stay where they are, the profile's hidden screens still hide, and floating windows go relative to the screens wherever they are. 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 once (`ft-float launch`, the same path as Launch as Standalone) and waits up to 30 seconds for its first window. Each window that shows up goes to the next entry's place. Once the first window has been up for 3 seconds (time for an app that restores its own windows to show them), ft-floatd launches the app again for each entry still waiting, and waits up to 30 seconds more. New windows are matched to the launch by process (or a child of it), or by desktop file name (found the same way as at capture): single-instance and D-Bus-activated apps open their windows from a process that was already running. - **Default at start.** The session script runs `ft-layout start --wait 90`. That opens the profile in `FT_PROFILE` or `default_profile` (screens, then the apps once ft-floatd is up), or runs `apply --wait` if there's none. Start in profile on the Layout & profiles page sets `default_profile` (`ft-layout default NAME|none`). Plasma's session restore is turned off in the session (`ksmserverrc`: `loginMode=emptySession`). - **Launcher entries.** Each profile gets `~/.local/share/applications/frametop-profile-.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. Otherwise it starts the desktop with `FT_PROFILE` set (`systemd-run`, as `desktops.sh start` does), which overrides `default_profile` for that start. That needs SteamVR to be running. +- **The quick reset.** Meta+Shift+R, the reset button on a screen's bar, the Reset Screen Layout menu entry, and a button mapped to Reset desktop screen layout run `ft-layout reset`: with a profile in use (`active`, and the custom arrangement), that's `use` on it again, so everything goes back as the profile has it. Without one, it's `apply`. - **The action.** `profile:NAME` in the input relay (it runs `ft-layout use NAME`) for key combinations, mouse buttons, and controller buttons, with or without pointer mode. Input Settings lists one "Open profile NAME" action per profile. - **Display Settings.** On the Layout & profiles page, the arrangement list has the profiles, which can be renamed and deleted. Open profile and Save as profile… are the page's actions. A profile's apps are listed with where each goes and a button to leave one out, plus which screens it hides. Start in profile picks the one the desktop starts with. The Visibility tab's Screens shown switches hide screens one at a time. diff --git a/docs/reference.md b/docs/reference.md index 0592a2d..51ee1a9 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -48,7 +48,7 @@ Every screen is an overlay named `frametop.screen.N` with five controls: - `.curve` bends the screen into a cylinder around you, using your current distance as the radius, or makes it flat again. - `.roll` rolls the screen when you drag it sideways, like a knob. It snaps level within 2.5°, and scrolling on it turns 5° per notch. - `.resize`, the tab on the bottom right corner, sets the width. Screens go down to 15 cm wide. -- `.reset`, left of the bar, puts every screen back in its layout around where you are now, like Meta+Shift+R (`ft-layout apply`). +- `.reset`, left of the bar, is the quick reset, like Meta+Shift+R (`ft-layout reset`): with a profile in use it opens that profile again, as Open profile does, and otherwise it puts every screen back in its layout around where you are now. The controls are sized from both the screen's width and its distance from you, follow the surface of a curved screen, and stay invisible until a laser or the 3D mouse's cursor lands on one or comes within about 1.5 times a button's size of it. While invisible they're still there, fully transparent, so SteamVR's laser can find them. They're translucent until a laser is on them, like SteamVR's own window controls. @@ -148,7 +148,7 @@ Device rules are saved in `~/.config/frametop-input.json`. `input-settings/insta ## Frametop Display Settings and ft-layout -When the desktop starts, its screens arrange themselves around where you're facing. You can move them by hand at any time and put them back with Meta+Shift+R, the reset button left of any screen's bar, the Reset Screen Layout menu entry, Arrange now in the app, or a mouse button mapped to Reset desktop screen layout. +When the desktop starts, its screens arrange themselves around where you're facing. You can move them by hand at any time and put them back with Meta+Shift+R, the reset button left of any screen's bar, the Reset Screen Layout menu entry, or a button mapped to Reset desktop screen layout. With a profile in use, these open it again, the same as Open profile: its screens, hidden screens, remote displays and apps. Arrange now in the app arranges the screens (and the profile's remote displays) without reopening its apps or hiding its hidden screens again. The desktop's own screen arrangement follows where the screens are around you, whatever their numbers: a screen you see to the left of another is to its left in Plasma too, so the pointer and dragged windows cross straight to it. Screens one above the other stack, and screens pinned to a wrist or your head come last. It's updated at startup, after arranging or saving the layout, and half a second after you let go of a screen you moved. With the headset off there's no head pose to go by, and the arrangement stays as it was. diff --git a/input/input-relay.py b/input/input-relay.py index 9b9a247..06103af 100755 --- a/input/input-relay.py +++ b/input/input-relay.py @@ -27,7 +27,7 @@ look; held, the pointer stops there and your head steers it (it stays put in you the release clicks; held still for half a second, it's a real press that your head drags ("gazekey left|right 1|0" to the helper; by default Meta+J and Meta+K, DEFAULT_KEY_BINDINGS), gaze_quickcal = the gaze service's one-dot check ("quickcal" to @ft_gazed), sens_up, sens_down, -layout_reset = put the desktop screens back in their saved layout, screens_toggle = hide or show the desktop screens, +layout_reset = open the profile in use again, or put the desktop screens back in their layout (ft-layout reset), screens_toggle = hide or show the desktop screens, keyboard_toggle = open or close Frametop's keyboard, float_toggle = float the desktop window under the pointer (else the active one) in VR, or put it back if it floats, dock_all = put every floating window back (both to ft-floatd, @frametop_float), spin_next and spin_prev = turn every panel in the @@ -91,7 +91,7 @@ default: only while no pass-through keyboard is connected; a program's uinput ke doesn't count), "button" (only the keyboard_toggle action opens it), or "never" (keyboard_toggle does nothing either). With "vr_keyboard_persist" (the default), it stays open when the text field loses focus, until its Close key, keyboard_toggle, or a layout reset -(ft-layout apply) closes it. +(ft-layout reset) closes it. Volume keys, from every device that has them (the headset's own buttons included), are handled here: wpctl steps the default output. Nothing else may see a volume key, @@ -658,7 +658,7 @@ class Pointer: pass # ft-screens not running elif name == "layout_reset": # Runs a few seconds and borrows the pointer; ft-layout refuses a second copy. - subprocess.Popen([FT_LAYOUT, "apply"], stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, + subprocess.Popen([FT_LAYOUT, "reset"], stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True) log("layout reset") elif name in ("sens_up", "sens_down"): diff --git a/layout/ft-layout-reset b/layout/ft-layout-reset index ccccf18..cae784f 100755 --- a/layout/ft-layout-reset +++ b/layout/ft-layout-reset @@ -1,4 +1,4 @@ #!/bin/bash # Reset Screen Layout's own program. Steam lists menu entries by program, so entries that # shared ft-layout (with different arguments) launched as one. -exec "$(dirname "$(readlink -f "$0")")/ft-layout" apply "$@" +exec "$(dirname "$(readlink -f "$0")")/ft-layout" reset "$@" diff --git a/layout/ft_layout.py b/layout/ft_layout.py index 3c775b1..c6fc274 100755 --- a/layout/ft_layout.py +++ b/layout/ft_layout.py @@ -64,6 +64,9 @@ is top left, then left to right. Usage (on the Frame host; Frametop Display Settings calls it too): ft-layout apply [--wait SECONDS] arrange every screen; --wait is for desktop start: wait for the screens, skip if "auto" is off + ft-layout reset a quick reset (Meta+Shift+R, a screen's reset button, a mapped + button): open the profile in use again, as use NAME does, or + else apply ft-layout capture save the current arrangement as the custom layout ft-layout save NAME save it as a named layout too, and use that; with the desktop's apps and hidden screens, as a profile @@ -1594,6 +1597,10 @@ def main(argv): else: log(f"kwin: {last}") open_apps(name, wait=90) # ft-floatd starts with Plasma + elif cmd == "reset": + layout = load_layout() + name = layout.get("active") if layout.get("mode") == "custom" else None + return main([argv[0], "use", name] if name in layout.get("layouts", {}) else [argv[0], "apply"]) elif cmd == "remote": remote_command(argv[2:]) elif cmd in ("pin", "unpin") and len(argv) >= 3: diff --git a/screens/vr.cpp b/screens/vr.cpp index 2c1dffa..f09ecac 100644 --- a/screens/vr.cpp +++ b/screens/vr.cpp @@ -11,8 +11,8 @@ // its centre (it snaps level within kRollSnap), or scroll on it for kRollStep steps. // - a resize tab on the bottom right corner: drag it to set the width (the height // follows the screen's resolution). -// - a reset button left of the bar: every screen back in its layout, around where you -// are now (`ft-layout apply`, like Meta+Shift+R). +// - a reset button left of the bar: the profile in use opened again, or every screen back +// in its layout, around where you are now (`ft-layout reset`, like Meta+Shift+R). // The controls are translucent, like SteamVR's own, and brighten under a laser. They // are invisible until a laser (a controller's, or the 3D mouse's) lands on or passes very close to // one of them (UpdateControls). @@ -2429,14 +2429,14 @@ void ft_vr_poll(void (*handle)(const struct ft_event *, void *), void *data) { } } } - // The reset button: every screen back in the layout, around where you are now - // (`ft-layout apply`, like Meta+Shift+R; it refuses a second copy). + // The reset button: the profile in use opened again, or every screen back in the layout, + // around where you are now (`ft-layout reset`, like Meta+Shift+R; it refuses a second copy). while (s.resetButton != vr::k_ulOverlayHandleInvalid && vr::VROverlay()->PollNextOverlayEvent(s.resetButton, &ev, sizeof ev)) { hover(6); if (ev.eventType == vr::VREvent_MouseButtonDown && ev.data.mouse.button == vr::VRMouseButton_Left) { std::printf("screen %d: reset the layout\n", index + 1); - RunLayout("apply"); + RunLayout("reset"); } else if (ev.eventType == vr::VREvent_MouseButtonUp) { EndDragsBy(ev.trackedDeviceIndex); ReleaseAwayBy(ev.trackedDeviceIndex, ev.data.mouse.button, handle, data);