37

What I want to be able to save the current positions of my applications, so when I'm going to open the same ones and run something they will rearrange as they were.

For example if I'm going to open a sublime and three terminal windows I would like to be able to save that somehow.

enter image description here

I don't care if it's an app or a command line tool, as long as I can easily save the positions of my apps.

I'm a big fan of Moom, but unfortunately it works only on MacOS and I really miss it when on Ubuntu. It supports more features and if you know something close to it on top of my main problem that's also fine.

Lipis
  • 473
  • @VitaliusKuchalskis Would the window layout do, or should it be exactly the correponding opened files? And what is your window manager? (Unity?) – Jacob Vlijm Jul 05 '15 at 20:18
  • I don't know what window layout are you talking about? I think there is or will be a tool for saving and loading position and size of windows per workspace. So far I found [wmctrl] (https://en.wikipedia.org/wiki/Wmctrl). But it would require to write scripts or change configurations to make this feature. So I'm, wondering if anyone already made this, and was kind enough to share it. – Subtle Development Space Jul 05 '15 at 20:46
  • @VitaliusKuchalskis could you take a look at this one: http://askubuntu.com/questions/631392/saving-and-restoring-window-positions/631467#631467 This assumes the window(s) stay open, but I assume you'd like to shut down the computer and resore the window positions after a restore size and position (layout). An important question then is if you are just referring to application windows, or also the files that were opened inside the windows. – Jacob Vlijm Jul 05 '15 at 21:51
  • Only the application windows of course. – Subtle Development Space Jul 05 '15 at 23:14
  • From your screenshot, you appear to like/use tiling formations for your windows. You should definitely give a tiling WM a try, for example i3. – nixpower Jul 06 '15 at 13:09
  • @VitaliusKuchalskis One more question: Do you need/like the solution to be across all workspaces (windows spread over-) or just one (would make quite a difference in coding :) )? – Jacob Vlijm Jul 07 '15 at 08:01
  • Well I think saving/loading a single workspace is much more handy. – Subtle Development Space Jul 07 '15 at 11:07
  • Hi @VitaliusKuchalskis See my answer, I made it work both ways :) Let me know if you encounter any difficulties. – Jacob Vlijm Jul 07 '15 at 18:08

6 Answers6

28

Note

The script was patched/fixed on January 16 2017, fixing for a few applications of which the process name differs from the command to run the application. Possibly, this occurs occasionally on applications. If someone finds one, please leave a comment.


Script to remember and restore the window arrangement and their corresponding applications.

The script below can be run with two options. Let's say you have the window arrangement as below:

enter image description here

To read (remember) the current window arrangement and their applications, run the script with the option:

<script> -read

Then close all windows:

enter image description here

Then to set up the last remembered window arrangement, run it with the option:

<script> -run

and the last remembered window arrangement will be restored:

enter image description here

This will also work after a restart.

Putting the two commands under two different shortcut keys, you can "record" your window arrangement, shutdown your computer and recall the same window arrangement after (e.g.) a restart.

What the script does, and what it does not

Run with the option -read

  • The script uses wmctrl to list all windows, across all workspaces, their positions, their sizes, the applications they belong to
  • The script then "converts" the window positions from relative (to the current workspace, as in the output of wmctrl) to absolute positions, on your spanning workspaces. Therefore it does not matter if the windows you want to remember are on only one workspace or spread over different workspaces.
  • The script then "remembers" the current window arrangement, writing it into an invisible file in your home directory.

Run with the option -run

  • the script reads the last remembered window arrangement; it starts the corresponding applications, moves the windows to the remembered positions, also with the help of wmctrl

The script does not remember the files that possibly might be opened in the windows, nor (e.g.) the websites that were opened in a browser window.

Issues

The combination of wmctrl and Unity has some bugs, a few examples:

  • the window coordinates, as read by wmctrl differs slightly form the command to position the windows, as mentioned here. Therefore the recalled window positions might slightly differ from the original position.
  • The wmctrl commands works a bit unpredictable if the edge of the window is very near either the Unity Launcher or the panel.
  • The "remembered" windows need to be completely inside a workspace borders for the wmctrl placement command to work well.

Some applications open new windows by default in the same window in a new tab (like gedit). I fixed it for gedit, but please mention it if you find more exceptions.

The script

#!/usr/bin/env python3
import subprocess
import os
import sys
import time

wfile = os.environ["HOME"]+"/.windowlist"
arg = sys.argv[1]

def get(command):
    return subprocess.check_output(["/bin/bash", "-c", command]).decode("utf-8")

def check_window(w_id):
    w_type = get("xprop -id "+w_id)
    if " _NET_WM_WINDOW_TYPE_NORMAL" in w_type:
        return True
    else:
        return False

def get_res():
    # get resolution and the workspace correction (vector)
    xr = subprocess.check_output(["xrandr"]).decode("utf-8").split()
    pos = xr.index("current")
    res = [int(xr[pos+1]), int(xr[pos+3].replace(",", "") )]
    vp_data = subprocess.check_output(["wmctrl", "-d"]).decode("utf-8").split()
    curr_vpdata = [int(n) for n in vp_data[5].split(",")]
    return [res, curr_vpdata]

app = lambda pid: subprocess.check_output(["ps", "-p",  pid, "-o", "comm="]).decode("utf-8").strip()

def read_windows():
    res = get_res()
    w_list =  [l.split() for l in get("wmctrl -lpG").splitlines()]
    relevant = [[w[2],[int(n) for n in w[3:7]]] for w in w_list if check_window(w[0]) == True]
    for i, r in enumerate(relevant):      
        relevant[i] = app(r[0])+" "+str((" ").join([str(n) for n in r[1]]))
    with open(wfile, "wt") as out:
        for l in relevant:
            out.write(l+"\n")

def open_appwindow(app, x, y, w, h):
    ws1 = get("wmctrl -lp"); t = 0
    # fix command for certain apps that open in new tab by default
    if app == "gedit":
        option = " --new-window"
    else:
        option = ""
    # fix command if process name and command to run are different
    if "gnome-terminal" in app:
        app = "gnome-terminal"
    elif "chrome" in app:
        app = "/usr/bin/google-chrome-stable"


    subprocess.Popen(["/bin/bash", "-c", app+option])
    # fix exception for Chrome (command = google-chrome-stable, but processname = chrome)
    app = "chrome" if "chrome" in app else app
    while t < 30:      
        ws2 = [w.split()[0:3] for w in get("wmctrl -lp").splitlines() if not w in ws1]
        procs = [[(p, w[0]) for p in get("ps -e ww").splitlines() \
                  if app in p and w[2] in p] for w in ws2]
        if len(procs) > 0:
            time.sleep(0.5)
            w_id = procs[0][0][1]
            cmd1 = "wmctrl -ir "+w_id+" -b remove,maximized_horz"
            cmd2 = "wmctrl -ir "+w_id+" -b remove,maximized_vert"
            cmd3 = "wmctrl -ir "+procs[0][0][1]+" -e 0,"+x+","+y+","+w+","+h
            for cmd in [cmd1, cmd2, cmd3]:   
                subprocess.call(["/bin/bash", "-c", cmd])
            break
        time.sleep(0.5)
        t = t+1

def run_remembered():
    res = get_res()[1]
    try:
        lines = [l.split() for l in open(wfile).read().splitlines()]
        for l in lines:          
            l[1] = str(int(l[1]) - res[0]); l[2] = str(int(l[2]) - res[1] - 24)
            open_appwindow(l[0], l[1], l[2], l[3], l[4])   
    except FileNotFoundError:
        pass

if arg == "-run":
    run_remembered()
elif arg == "-read":
    read_windows()

How to set up

Before you start, make sure wmctrl is installed:

sudo apt-get install wmctrl

Then:

  1. Copy the script into an empty file, save it as recall_windows in ~/bin. Create the directory if necessary. If the directory didn't exist yet, run either source ~/.profile or log out/in after you created the directory. It will now be in $PATH
  2. Make the script executable (!).
  3. Now open a few windows, gedit, firefox or whatever, and test-run the script in a terminal by running the command (no path prefix needed):

    recall_windows -read
    
  4. close the windows. Now run in a terminal:

    recall_windows -run
    

Your window setup should now be restored

If all works fine, add two commands to shortcut keys: Choose: System Settings > "Keyboard" > "Shortcuts" > "Custom Shortcuts". Click the "+" and add the commands:

recall_windows -read

and

recall_windows -run

to two different shortcut keys

Jacob Vlijm
  • 83,767
  • 2
    Hah! Just reading the first paragraph and I knew it was one of yours! (upvoted) – Fabby Jul 07 '15 at 20:57
  • @Fabby and celebrating my holiday :) – Jacob Vlijm Jul 07 '15 at 20:59
  • Looks great! Now one could easily add a second param for a setting name and then store/restore different environments, clientA, clientB, home, .. cool! – Bachi Oct 23 '15 at 16:38
  • different versions would be cool, be great if it could close the program. Thinking if there is anything on screen when the wife comes in I could easily make it look like i'm working! – Jamie Hutber Aug 18 '16 at 09:30
  • Could you perhaps take a look at this workspace-related problem ? Thanks!! – nutty about natty Sep 27 '16 at 08:45
  • This doesn't work for ubuntu 16.04 as the application commands are not correctly saved. I get the following errors: /bin/bash: chrome: command not found /bin/bash: gnome-terminal-: command not found. Would also be nice if you could release this on git! – hugo der hungrige Jan 14 '17 at 14:24
  • @hugoderhungrige fixed! the cause was a changed gnome-terminal process name in 16.04. I might prepare it to move it to Launchpad soon :) – Jacob Vlijm Jan 15 '17 at 10:30
  • @JacobVlijm thank you very much for the quick fix! The terminal is now successfully restored. I still get an error for chrome however:/bin/bash: chrome: command not found and there is also a new one: (gnome-terminal.real:21110): Gtk-WARNING **: Theme parsing error: gtk-contained.css:2750:55: Not a valid image – hugo der hungrige Jan 15 '17 at 16:45
  • Hi @hugoderhungrige also fixed. This happens on some applications of which the process name differs from the command to run the application. The Gtk warning is safely ignored. It happens often when applications are run from the terminal. – Jacob Vlijm Jan 16 '17 at 11:30
  • Thanks a bunch for writing this useful little script! On my XFCE (Xubuntu 16.04) setup, wmctrl -d returns N/A viewports for the inactive workspaces (which causes the script to throw), and the horizontal and vertical offsets needed to be tweaked slightly. Pushed a gist with these small modifications in case it is useful to other XFCE users! – tinybike Aug 11 '17 at 21:26
  • Hi @tinybike, great! Please give attribution if you post it. – Jacob Vlijm Aug 11 '17 at 23:51
  • This looks really useful - but for me (on 17.10) running netbeans, it just saves the command 'java' and not the entire command line needed to fire up netbeans. This means i just get a 'java' usage message and no IDE. – FreudianSlip Dec 23 '17 at 09:37
  • It's working (Arch Linux XFCE), but I've got an error: Traceback (most recent call last): File "./bin/test", line 85, in <module> run_remembered() File "./bin/test", line 80, in run_remembered open_appwindow(l[0], l[1], l[2], l[3], l[4]) File "./bin/test", line 64, in open_appwindow w_id = procs[0][0][1] IndexError: list index out of range – ThePhi Apr 25 '18 at 09:37
  • How to make it support the existence of windows opened as root and others by the default user? – Vitor Abella Sep 06 '18 at 00:40
  • On 19.10 it failed to restore system monitor at all. Other windows were in wrong places. either by a little or a lot. I have a dual 1920x1080 monitor setup with each at different hights, giving a virtual X Screen of 3840x1670 pixels (1016x442 millimeters). Cool script! – ahcox Nov 26 '19 at 13:57
9

I wrote a little library/command line tool which allows saving and restoring sessions and has support for different monitors setups as well as virtual desktops.

Installation

npm install -g linux-window-session-manager

Usage

Save the current session to ~/.lwsm/sessionData/DEFAULT.json

lwsm save

Save the current session to ~/.lwsm/sessionData/my-session.json

lwsm save my-session   

Restore the session from ~/.lwsm/sessionData/DEFAULT.json

lwsm restore

Restore the session from ~/.lwsm/sessionData/my-session.json

lwsm restore my-session   

Gracefully close all running apps before starting the session

lwsm restore --closeAllOpenWindows

Check it out: https://github.com/johannesjo/linux-window-session-manager

2

there is no such program. You may install compiz cub:

sudo apt-get install compiz compizconfig-settings-manager compiz-fusion-plugins-extra compiz-fusion-plugins-main compiz-plugins

and follow this how-to

the compiz is the most advanced desktop tool for unity/gnome

1

I could not post updated script from the top answer as a comment. This is a slightly updated version of that script. The main change is that if the application exists, it will be simply re-positioned instead of re-launched. Otherwise it is a great script. I used it on Lubuntu 16.04 and now on 18.04. Thanks for writing it.
I have posted this script in my repository on github: https://github.com/xwin/savelayout
A calibration function was added, where the tool will automatically find offsets to keep restored window in the same place.

xwin
  • 11
  • 2
1

I don't know of a simple way of achieving this.

However, I rarely need that for a very simple reason: suspend. Suspend and hibernation are your friends. Not only do you save window positions, but you also save the whole state of your system. I rarely switch off the computer completely, except to reload a new kernel version.

January
  • 35,952
  • Well I'm not switching it of or anything.. but I'm opening you know new terminals, another project, the browsers is getting closed or something, etc.. the screenshot was just an example.. – Lipis Sep 27 '12 at 10:48
  • Well, yes. I have desktops that I haven't touched for weeks now which contain several terminal windows, browser windows, R graphic display windows that all relate to a particular project. – January Sep 27 '12 at 10:52
  • Suspend is the solution I use for my notebook, however, for a Desktop PC is a bit more complicated, since it's necessary to have a nobreak. – cantoni Aug 06 '15 at 11:44
  • Not much help after a reboot. – Jeff Learman Nov 18 '20 at 13:43
  • @Jacob Vlijm Please look at the diagnostic (failure) output when I attempted to use your script (posted in the URL title in my question). 20.04 LTS current, MATE production current. My apologies for violating "etiquette" on this list. I do know that whenever I post a "workaround" I personally cannot "support" the workaround as I do not have technical support staff nor personal free time to be a maintainer. My apologies.https://askubuntu.com/questions/1318258/how-to-save-and-restore-a-desktop-layout-in-mate – Yasha Karant Mar 01 '21 at 19:38
0

https://ubuntu-mate.community/t/how-to-save-and-restore-a-desktop-icons-positions-in-mate/23651/22

has a solution to the above issue for MATE 20.04 LTS; the solution was written by moof-moof . At some point, the MATE developers/maintainers may incorporate a utility that will allow one to save and restore the desktop configuration of the icons selected by the user.