Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Layout save/restore #1005

Closed
ddevault opened this issue Dec 23, 2016 · 49 comments
Closed

Layout save/restore #1005

ddevault opened this issue Dec 23, 2016 · 49 comments

Comments

@ddevault
Copy link
Contributor

Layout save/restore is an i3 feature that serializes your layout to JSON and attempts to arrange windows in the same way in a later session. First step is to research how i3 does it and propose some strategies for implementing it in sway.

@cyphar
Copy link
Contributor

cyphar commented Dec 27, 2016

I did a bit of research using the documentation as a reference. Effectively the way this is implemented (I don't use this feature at all) is this:

  • When generating the i3-save-tree output it includes all of the obvious information about container placement and so on -- which for us would just be a matter of recursively walking down the tree.

  • However, it includes "swallowing" information which is effectively a set of regular expressions that matches WM_CLASS (which is a tuple of (instance, class)), _NET_WM_NAME (title) and WM_WINDOW_ROLE (window role). When a program is started in an i3 session and it matches all of the "swallowing" information, it will place that program in the loaded pane.

  • The swallowing information is actually an array, so you can create OR'd matches.

  • Note that the output of i3-save-tree is not actually standard-conformant JSON. IMO we should not follow their lead, and we should always output standard compliant JSON.

So from what I can see there are a few things we need to implement and/or consider:

  1. Is there a nicer way of implementing "swallowing" in Wayland than in X? The above seems quite hacky to me.
  2. What is the analogy for WM_CLASS, _NET_WM_NAME and WM_WINDOW_ROLE in the Wayland (and wlc) protocols?
  3. We need to implement the concept of a "swallowing" window, which is effectively an empty window which is "busy".
  4. Need to create protocol endpoints for communicating about generating the JSON blob.
  5. Need to augment the configuration to allow you to append_layout (source a JSON blob and add it to the current layout).

@ddevault
Copy link
Contributor Author

I've decided that we are just not going to support this i3 feature, ever.

@mahmoudhossam
Copy link

@SirCmpwn Any specific reason why?

@ddevault
Copy link
Contributor Author

ddevault commented Oct 8, 2017

It's too complicated and hacky for too little benefit. I don't like the design constraints it imposes upon sway.

@hyiltiz
Copy link

hyiltiz commented Jan 26, 2018

Would you reconsider your decision? This feature is actually quite useful in some (could even say rare) professional settings, e.g. setting up a music production with a midi mixer, synthesizer, sequencer, and composition programs open in their specific layout every time, or just general software development with editors, compilers, debuggers and test windows.

While there is no point in being i3 compliant on this, I think a good discussion in trying to come up with a better solution would be quite beneficial.

@ddevault
Copy link
Contributor Author

No, I won't reconsider. However, you should be able to accomplish something similar with a script that starts up the software you need and arranges it appropriately via IPC.

@hyiltiz
Copy link

hyiltiz commented Jan 26, 2018

I would love to say "Would you like to reconsider whether you would like to reconsider 'No, I won't reconsider' ", but that might easily overflow the buffer if not implemented correctly. Anyway, I do think writing a script for that should be straightforward so thanks!

@flux242
Copy link

flux242 commented Oct 21, 2018

it's a shame that this great i3 feature is dropped simply because "I'm too lazy to implement something I personally do not use". Please tell me how am I suppose to do swallowing in a script? How do I assign a program window to a specific layout window using IPC? If I start a program I only know its process id and what is available in the /proc/proc_id.

@ddevault
Copy link
Contributor Author

It's too complicated and hacky for too little benefit. I don't like the design constraints it imposes upon sway.

This is why it was dropped, not because, quote, "I'm too lazy to implement something I personally do not use." You can use IPC to move a window to a specific workspace over IPC with criteria. You can split the window and move things around, too. The pid associated with a window is included in the IPC_GET_TREE output, along with a unique ID you can use for criteria.

@flux242
Copy link

flux242 commented Oct 21, 2018

give an IPC command example please of moving a program window into a specific layout window on a workspaceX

@ddevault
Copy link
Contributor Author

To arrange three windows like this on an empty workspace:

xz
yz
  1. Execute the 3 processes and wait for their windows to appear (you'll be notified via IPC_EVENT_WINDOW)
  2. Use IPC_GET_TREE and find the container IDs which map to those pids
  3. If they're not already all on the desired workspace, issue [con_id=$id] move to workspace $ws commands until they are
  4. [con_id=x] splitv
  5. [con_id=y] move left

@flux242
Copy link

flux242 commented Oct 21, 2018

right, why there can't be a method that does exactly this if it is so easy? It would just have to parse a layout file additionally, right? Now using your approach what would I do if I wanted to change my layout a bit, like resize here and there provided that my layout is a little bit more complex rather that just 3 windows? Currently I just have to resize and overwrite my layout file. And btw I'm inside of a bash script so how do I get an IPC notification?

@ddevault
Copy link
Contributor Author

ddevault commented Oct 21, 2018

right, why there can't be a method that does exactly this if it is so easy?

See

It's too complicated and hacky for too little benefit. I don't like the design constraints it imposes upon sway.

If you want to write a script which implements i3 layout save/restore as an external program, you should do so, but no one is going to write it for you. This project is run by volunteers, and no one is volunteering to do this.

And btw I'm inside of a bash script so how do I get an IPC notification?

By not being in a bash script

@flux242
Copy link

flux242 commented Oct 21, 2018

nope, I'm not going to implement a script because

It's too complicated and hacky

without proper support from underlying framework. Your script "example" could only work for very simple layouts. It's because if you have a generic layout description you'd have to start with a specific program window traversing the layout up the tree trying to figure out where that window would land in the end and how the inverted IPC command chain would look like.

btw feature that allows restoring a complex working environment (up to starting terminals with specific commands) just by a keyboard shortcut brings rather huge benefit than "too little benefit". Lacking such a feature is a road block for me => no switching from i3. Chusikowski

@9ary
Copy link
Contributor

9ary commented Oct 21, 2018

I agree that i3's implementation of this feature has its own set of shortcomings. However, the proposed alternative of using IPC feels rather clunky since all it allows is akin to replaying a keyboard macro.

A more powerful alternative would be to extend the API to allow passing a tree of existing windows referenced by ID or criteria, with optional hints for layout and sizing, and have sway apply that atomically.
This would feel much cleaner than i3's swallow windows (which can only capture windows that do not yet exist), while providing equivalent, if not more flexible functionality.

Note that my proposal differs from i3 in two ways:

  • i3 expects a path to a JSON file containing the layout. This is dumb, the entire layout should be passed over IPC instead. Clients may choose to read a layout from a file, but that's up to them.
  • When you call append_layout, i3 spawns placeholder windows, and stores the criteria in the container tree. When a new window is mapped and i3 attempts to manage it, it searches the tree for a matching placeholder and substitutes the first positive match. This requires special handling in various components of the window manager, and can't operate on existing windows.
    My proposal is rather self-contained: since it should operate on existing windows, everything can be done immediately in the command handler, without having to spawn placeholders and/or store additional metadata in the tree.

@ddevault
Copy link
Contributor Author

I would consider a patch, but I'm not interested in doing this myself and I'm worried about the complexity/value tradeoff.

@9ary
Copy link
Contributor

9ary commented Oct 21, 2018

That's all I needed to hear. I would gladly write this feature myself, but I'm currently focusing on other projects so i3 is still my daily driver for now. I'll probably come back to it later, unless someone else is interested in doing it before me.

@JonnyHaystack
Copy link

I agree that i3's implementation of this feature has its own set of shortcomings. However, the proposed alternative of using IPC feels rather clunky since all it allows is akin to replaying a keyboard macro.

A more powerful alternative would be to extend the API to allow passing a tree of existing windows referenced by ID or criteria, with optional hints for layout and sizing, and have sway apply that atomically.
This would feel much cleaner than i3's swallow windows (which can only capture windows that do not yet exist), while providing equivalent, if not more flexible functionality.

Note that my proposal differs from i3 in two ways:

  • i3 expects a path to a JSON file containing the layout. This is dumb, the entire layout should be passed over IPC instead. Clients may choose to read a layout from a file, but that's up to them.
  • When you call append_layout, i3 spawns placeholder windows, and stores the criteria in the container tree. When a new window is mapped and i3 attempts to manage it, it searches the tree for a matching placeholder and substitutes the first positive match. This requires special handling in various components of the window manager, and can't operate on existing windows.
    My proposal is rather self-contained: since it should operate on existing windows, everything can be done immediately in the command handler, without having to spawn placeholders and/or store additional metadata in the tree.

I completely agree with all of this. The placeholder system in i3 is not very good and you have to use hacks like unmapping and remapping windows with xdotool to get a layout to apply to existing windows.

Let me know when you get time to start working on it. I'd be eager to help out.

@JonnyHaystack
Copy link

JonnyHaystack commented Nov 1, 2019

I've recently been looking at the code that i3 uses when restoring a layout.

json_content_t content = json_determine_content(buf, len);
LOG("JSON content = %d\n", content);
if (content == JSON_CONTENT_UNKNOWN) {
    ELOG("Could not determine the contents of \"%s\", not loading.\n", path);
    yerror("Could not determine the contents of \"%s\".", path);
    goto out;
}

Con *parent = focused;
if (content == JSON_CONTENT_WORKSPACE) {
    parent = output_get_content(con_get_output(parent));
} else {
    /* We need to append the layout to a split container, since a leaf
     * container must not have any children (by definition).
     * Note that we explicitly check for workspaces, since they are okay for
     * this purpose, but con_accepts_window() returns false for workspaces. */
    while (parent->type != CT_WORKSPACE && !con_accepts_window(parent))
        parent = parent->parent;
}
DLOG("Appending to parent=%p instead of focused=%p\n", parent, focused);
char *errormsg = NULL;
tree_append_json(parent, buf, len, &errormsg);
if (errormsg != NULL) {
    yerror(errormsg);
    free(errormsg);
    /* Note that we continue executing since tree_append_json() has
     * side-effects — user-provided layouts can be partly valid, partly
     * invalid, leading to half of the placeholder containers being
     * created. */
} else {
    ysuccess(true);
}

So it checks for the type of the data it loads from the file whose path you pass to append_layout, and defaults to con if it doesn't find any "type" properties.

If the content is of type "workspace", it appends the layout to the currently focused output's workspace array. If it is a con, it searches upwards from the currently focused node until it finds a workspace, then it appends the layout to the workspace's child array.

The fact that it only appends nodes to an array and does not modify or replace existing nodes means that it is actually quite clean. I've come to appreciate the simplicity of this approach, as opposed to rewriting the existing tree.

The only part of their implementation that still seems bad to me is that placeholder container matching is only done when windows are created. We could just implement layout restoring almost exactly the same as i3, but just search the whole workspace for matching windows right after the layout is appended, instead of when new windows are mapped.

Or we could have the best of both worlds and check for matches after layout appending, and on window creation, for those use cases where placeholder windows/deferred swallowing is desirable (these cases really do exist). The caveat of this is obviously that it's less self-contained because of having to monitor window creation events.

Either way it doesn't seem like this would be that hard to implement.

Edit:
Also FWIW, layout saving is already a non-issue at this point because we can already get the entire window tree using swaymsg -t get_tree or the same thing through IPC. There's no real point in bundling an equivalent to the crappy i3-save-tree script into sway.

All that's needed right now is:

  • A function that simply receives a partial window tree (in json format) and inserts it in the appropriate location in the actual tree
    • swaymsg/IPC command must be provided to call into this
    • "Appropriate location" can be decided in the same way that i3 decides it, which is very simple and is explained above
    • This is literally just appending the deserialised json to an array, it doesn't get much simpler than this
  • Now I'm not sure exactly how the tree is managed in sway, or whether things get pruned automatically, but leaf nodes that have a "swallows" attribute would need to stay there at least long enough that we can check for matches
  • Now, after the partial tree is inserted into the main tree, for each "real" window in the workspace we want to check if there is a matching placeholder window
    • Might be an option to make a list of the leaf nodes (placeholders) in the partial tree as we are inserting it, just to make things easier
    • When a match is found, just replace the placeholder window with the matching real window
  • At this point it may be fine to prune any remaining placeholder windows. That's a less important implementation detail.

@flux242
Copy link

flux242 commented Dec 29, 2019

The only part of their implementation that still seems bad to me is that placeholder container matching is only done when windows are created. We could just implement layout restoring almost exactly the same as i3, but just search the whole workspace for matching windows right after the layout is appended, instead of when new windows are mapped.

lets say I have 100 windows in my layout. Right now I have a simple script that loads specific layout and then starts 100 programs in the background. Your suggestion would imply that I have to implement additional 'wait_for_all_100_windows_be_opened_before_loading_layout' and which is more important 'do_x_if_window_y_wont_appear_within_z_sec' logics. Is that what you're saying @JonnyHaystack ?

@norcalli
Copy link
Contributor

@ddevault your assertion that this could be easily done in a script is incorrect. I know because I tried it. sway doesn't have good enough commands to make it easy to be accomplished for an arbitrary layout. For example, there is no "move to this container." Instead you have to make a mark, and then move to that mark. This ends up being error prone. Another thing I tried is creating a placeholder window so that I knew its con_id and I could target it without needing to use marks as a place to drop windows on. This too is error prone as the timing required to make sure that windows transfer is not so straightforward.

Finally, there's also not a good way of modifying the layout of windows without focusing them. You may thing that criteria targetting would work, but it doesn't work reliably. And this is all from 2020 when I tried it. At the time that you made the suggestion to make a script in 2017, I am certain sway was in an even less adequate state to achieve it.

Maybe I'm not smart enough to get it done, but then again that also proves the value of having it built into sway or having an official solution.

I could try to spend more time on it, but I have already sunk 3 hours into it. It's likely not worth it to do in such a hacky way. And if you want dozens of other people to spend/waste their time simply because you are still under the misconception that this is "easy to do" then I think it would be fair to apologize for the time that others have spent and will spend time on this.

@emersion
Copy link
Member

Ref #3022

@JonnyHaystack
Copy link

The only part of their implementation that still seems bad to me is that placeholder container matching is only done when windows are created. We could just implement layout restoring almost exactly the same as i3, but just search the whole workspace for matching windows right after the layout is appended, instead of when new windows are mapped.

lets say I have 100 windows in my layout. Right now I have a simple script that loads specific layout and then starts 100 programs in the background. Your suggestion would imply that I have to implement additional 'wait_for_all_100_windows_be_opened_before_loading_layout' and which is more important 'do_x_if_window_y_wont_appear_within_z_sec' logics. Is that what you're saying @JonnyHaystack ?

No, ideally swallowing would occur in 3 situation:

  • When windows are mapped (for the scenario that you described)
  • When window title/class/other potential swallow criteria change (because most programs will at least not immediately set the title to what you're trying to match by)
  • After a layout is loaded by append_layout (so that you can apply a layout to existing windows)

All three of these could be handled in different ways, but here's my take:

  • I would suggest that when windows are mapped, the behaviour should be similar to that of i3, so that windows can be swallowed to a different workspace from the one they appear on.
  • For title etc changing, it makes sense to handle it the same way
  • After a layout is appended, you could either look at all windows in the entire tree, or just the windows in the current workspace. In i3 it's possible to unmap and remap every window in the workspace to trigger a swallow, which is a bit hacky but works. You couldn't unmap/remap windows from other workspaces because that would mess up the layout of all your other workspaces and would move every window to the current workspace, and it also wouldn't make sense to do that anyway if you're just trying to apply a layout on a specific workspace. So I think it would make sense to only care about windows on the current workspace for swallowing directly after appending a layout.

For the first and second situation, there should not be too much added complexity, but it would presumably require small modifications to event handlers in the main codebase. I could be wrong here because I haven't looked at sway's code in depth.

For the third situation, there could be a fair bit of added complexity in having to recurse over the target workspace's tree, but I think this could be kept separate from the main codebase, as part of the append_layout command's implementation. Alternatively if there's any tool for wayland/sway that lets you unmap/remap the windows in the workspace similar to what you can do with xdotool, that would be acceptable and situation 3 could be ignored, but again, that's kinda hacky.

@Hubro
Copy link

Hubro commented Dec 18, 2020

Are there any good options for restoring layouts at this point? I'm a software developer and I use append_layout daily to start my work environments, as well as position my chat windows (I use like 8 different chat clients.) The lack of this functionality is my main blocker with Sway at the moment.

If anybody has any good workarounds using IPC I would love to see some examples. I'm not savvy enough with Sway to have any clue what ddevault is talking about when he says things like IPC_GET_TREE.

@yellowhat
Copy link

yellowhat commented Dec 25, 2020

Hi,
I managed to use i3ipc-python to open a multiple windows at the same time in a certain order:

#!/usr/bin/env python3

from i3ipc import Connection

conn = Connection()

def run(cmd):
    conn.command(cmd)
    ilen = len(conn.get_tree().workspaces()[-1].descendants())
    while len(conn.get_tree().workspaces()[-1].descendants()) == ilen:
        True
    con_id = max(i.id for i in conn.get_tree().workspaces()[-1].descendants())
    return con_id

conn.command("workspace file")
con_id1 = run("exec wayst -e sf /")
con_id2 = run("exec wayst -e sf ~/Downloads")
con_id3 = run(f"[con_id={con_id1}] focus; splitv; exec wayst -e sf /tmp")
con_id4 = run(f"[con_id={con_id2}] focus; splitv; exec wayst -e sf /mnt")
conn.command(f"[con_id={con_id1}] focus")

Any suggestions are welcome

@Hubro
Copy link

Hubro commented Dec 27, 2020

Hi,
I managed to use i3ipc-python to open a multiple windows at the same time in a certain order:

#!/usr/bin/env python3

from i3ipc import Connection

conn = Connection()

def run(cmd):
    conn.command(cmd)
    ilen = len(conn.get_tree().workspaces()[-1].descendants())
    while len(conn.get_tree().workspaces()[-1].descendants()) == ilen:
        True
    con_id = max(i.id for i in conn.get_tree().workspaces()[-1].descendants())
    return con_id

conn.command("workspace file")
con_id1 = run("exec wayst -e sf /")
con_id2 = run("exec wayst -e sf ~/Downloads")
con_id3 = run(f"[con_id={con_id1}] focus; splitv; exec wayst -e sf /tmp")
con_id4 = run(f"[con_id={con_id2}] focus; splitv; exec wayst -e sf /mnt")
conn.command(f"[con_id={con_id1}] focus")

Any suggestions are welcome

Aha, I see... Your example would probably work in some simple cases, but I can already see it falling apart with applications like Discord, where it displays a loading splash screen for a little while before starting, and often also starts an updater. Although this could be worked around by waiting for a new window that matches certain filters.

Also it has to open applications one at a time, which could be potentially very slow, but getting around that would be pretty complicated 🤔 I'm imagining a script that would launch a bunch of applications simultaneously, then loop on get_tree and resolve each new window ID based on a filter and set each new window to floating, then a different thread would execute an ordered list of movement commands to place the windows in the right position after "unfloating" them. The script could be made reusable by a simple DSL, something like this:

from sway_layout import Row, Column, Application, launch

launch(
    Column(
        Application("chromium --app=https://messenger.com", cls="Chromium", instance="^Messenger$"),
        Application("slack", cls="Slack", instance="^Messenger$"),
    ),
    Column(
        Application("viber", cls="^Viber$"),
        Row(
            Application("discord", cls="^discord$", instance="^discord$", window_type="normal", name="- Discord$"),
            Application("teamspeak3", cls="^TeamSpeak3$"),
        )
    )
)

I don't see any reason this wouldn't be possible to implement. I will probably take a swing at it when I get some free time, I am really missing this feature.

@9ary
Copy link
Contributor

9ary commented Dec 27, 2020

This is my take on the problem, it's definitely not bulletproof but it works most of the time for me. https://github.com/9ary/dotfiles/blob/master/i3/ws-1.py
Again, the ability to pass a tree into sway and have it arrange windows accordingly would help a lot for writing external tools to do this. It doesn't even need to be smart and match criteria, just identify windows by their ID and leave the rest up to the application.

@tgunnoe
Copy link

tgunnoe commented Mar 18, 2021

@9ary can you explain a bit to me how this works? I couldn't get your example working after a day or so, but it could be due to my lack of experience with the sway IPC.

in particular, https://github.com/9ary/dotfiles/blob/master/i3/ws-1.py#L83

event_loop = asyncio.create_task(sway.main())

It seems to never respond, or report back anything after it hits this point. What am I doing wrong?

@9ary
Copy link
Contributor

9ary commented Mar 18, 2021

Yeah, it blocks until every leaf node has a match, so it won't do anything until all members of your layout exist (I would have to implement placeholders otherwise, which is significantly more work, and my workflow doesn't require them).

@tgunnoe
Copy link

tgunnoe commented Mar 26, 2021

Thanks for the explanation. Got it working after learning more about the script and get_tree

@JonnyHaystack
Copy link

This is my take on the problem, it's definitely not bulletproof but it works most of the time for me. https://github.com/9ary/dotfiles/blob/master/i3/ws-1.py
Again, the ability to pass a tree into sway and have it arrange windows accordingly would help a lot for writing external tools to do this. It doesn't even need to be smart and match criteria, just identify windows by their ID and leave the rest up to the application.

@9ary How would you match the window by ID considering the ID the next time you launch the program will be different from when you saved the layout?

@9ary
Copy link
Contributor

9ary commented Mar 26, 2021

The point is that an external program could query the tree to identify the desired windows, and even spawn placeholder windows, effectively taking the complexity out of sway, and enabling more complex use cases.

@JonnyHaystack
Copy link

Right, I get you

@Nama
Copy link

Nama commented Aug 23, 2022

I expanded some example script to make it work. The requirements are in the file. Only issue is that multiple windows of an application aren't handled.
https://gist.github.com/Nama/55786d0b2a8349d11f9013fa1a86e6b1

@MNolan147
Copy link

@Nama This script doesn't seem to work for me. It is able to save the tree, but can't restore it from the file.

@Nama
Copy link

Nama commented Sep 14, 2022

Do you get any error? If yes, pls post.
Do you have more than one of the same monitor? If yes, tell me the names of them from swaymsg -t get_outputs.

@MNolan147
Copy link

@Nama No error, no duplicate monitors. When it finishes is says that the session was successfully loaded, but no programs were started and no already open programs were moved to the workspace they should be moved to.

@Nama
Copy link

Nama commented Sep 14, 2022

The script doesn't start any programs.

Can you send the tree json the script saved?

@Nama
Copy link

Nama commented Sep 14, 2022

Please send such long text as file. You should edit and delete your json tree now.
My script makes use of the window_properties, which your tree doesn't have once.

              "window_properties": {
                "class": "firefox",
                "instance": "Navigator",
                "title": "Variable refresh rate - ArchWiki — Mozilla Firefox",
                "transient_for": null,
                "window_role": "browser",
                "window_type": "normal"
              }

I don't know why. The only differences seem to be that window_properties are missing and (probably for the same reason) "window" is always null. My applications have all an ID as the value.
Even tho, "visible" says true, these values are missing for you. That you (somehow) hid the windows would have been my first guess.

Do you have any workspaces which aren't vertically aligned? I have only one vertical workspace, but the window_properties aren't missing there. For me, only Sublime Text is missing these values.

My version of sway is 1:1.7.

@MNolan147
Copy link

Apologies for the long text. I've attached the file to this message.
I've run swaymsg -t get_tree and the window_properties don't appear there either

Workspace Tree:
workspace_startup_tree.txt

@Nama
Copy link

Nama commented Sep 14, 2022

Figured it out.
Only applications using xwayland get the "window_properties" data.
And somehow, ever of my apps do this except Sublime Text. (Firefox needs even a env var)

My script is useless.

@MNolan147
Copy link

You could change the script to also check for the app_id property, which seems to perform the same function as window_propeties["class"].

@MNolan147
Copy link

And to move multiple windows of the same type, you could use the id property to distinguish between the different windows i.e., if the layout has 2 terminals that it needs to move to specific workstations, it loops through open terminals and moves them based on their id.

@Nama
Copy link

Nama commented Sep 15, 2022

Oh yeah, app_id seems useful, thx.

Isn't the id of the node always another one, so we can't compare them?
Or do you mean like, keeping track of, which id was moved already and skipping to never moved ones?

@Nama
Copy link

Nama commented Sep 15, 2022

Wayland apps are now working.
You need i3ipc from Github, newest commit. Last release is lacking that feature -.-
Also made dunstify and sh optional, without the need to edit the script.

I'll look into handling multiple windows now.
https://gist.github.com/Nama/55786d0b2a8349d11f9013fa1a86e6b1

@roselandgoose
Copy link

I've modified @Nama's script above to support multiple windows of the same program - for my many firefox windows...
I've also made the layout file argument optional.

https://git.sr.ht/~roselandgoose/Sway_Layout_Restore/blob/main/sway_workspaces.py

@Nama
Copy link

Nama commented Oct 14, 2022

Oh that's nice! Thank you, @roselandgoose. I created a repo without your changes, so you can PR your changes. Would be more convenient to work on it in a repo (and move the discussion from this issue XD).
https://github.com/Nama/sway-workspaces

@JoshElias
Copy link

As a TLDR; for people looking for this feature, the sway dev doesn't want to add it but Nama has created a plugin that somewhat recreates i3-save-tree.

@mishurov
Copy link

@Nama I can traverse recursively a saved json from GET_TREE and launch apps using app_id, or map app_id to an executable, instead of moving existing apps. I wrote a script for my personal use with my own unbloated sway ipc but If you're interested, you might take a look how I do it.
https://github.com/mishurov/applets/blob/master/sway_restore_workspace/srws.py

python3 ./srws.py --workspace myworkspacename --save ./test.json
python3 ./srws.py --load ./test.json

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests