Ubuntu – Saving and restoring window positions

displaymultiple-monitorsunitywindow-managerwmctrl

I'm experiencing a bug where when the machine comes back from standby a monitor connected via displayport (MST) does not come back on, however the windows are still in the area it cover if it did come on.

The only way to recover from this short of a restart is to disable and reenable the display now I can do that via the command line just fine but when the display is disabled all the windows get rearranged onto the remaining screen.

Is there a way to persist the window positions and restore them so my script could do this sequence:-

  1. enumerate windows, store positions
  2. display off (messing up positions)
  3. display on (making the display work again, yay!)
  4. restore position of the windows

I can do step 2 and 3 just fine but I'm lost for step 1 and 4

I know I can force a position per app but that's not what I want as I could be doing different things and have the same apps in different positions, I also have multiple terminals open, I need the current positions storing somehow and restoring.

Any help would be appreciated!

Best Answer

Take a "snapshot" of the window arrangement and restore it

The script below can be used to get the current window positions of all "normal" windows (run with the argument -get), or restore the last window arrangement (run with the argument -restore).

As explained here, using wmctrl i.c.w. Unity has some issues. If it is satisfying in your situation is to decide.

How to use

The script uses wmctrl:

sudo apt-get install wmctrl

Then:

  1. Copy the script into an empty file, save it as window_arrange.py
  2. Run it by:

    python3 /path/to/window_arrange.py -get
    

    to "record" the current window arrangement, and

    python3 /path/to/window_arrange.py -restore
    

    to restore the last "recorded" window arrangement

The script:

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

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 read_windows():
    w_list =  [l.split()[:6] for l in get("wmctrl -lG").splitlines()]
    relevant = [(" ").join(w) for w in w_list if check_window(w[0]) == True]
    with open(wfile, "wt") as out:
        for item in relevant:
            out.write(item+"\n")

def restore_windows():
    try:
        wlist = [l.split() for l in open(wfile).read().splitlines()]
    except FileNotFoundError:
        pass
    else:
        for w in wlist:
            try:
                cmd = "wmctrl -ir "+w[0]+" -e 0,"+(",").join(w[2:])
                subprocess.Popen(["/bin/bash", "-c", cmd])
            except:
                pass

if arg == "-restore":
    restore_windows()
elif arg == "-get":
    read_windows()
Related Question