How to recover offscreen window after disconnecting second monitor?

Move all windows into the visible area

As proposed/requested in a comment, the script below will move all "normal" windows to the visible area on the current workspace.

The solution is a workaround; the screen info is updated correctly, given the difference in the output of xrandr, before and and after disconnecting. The reason why the windows do not move by themselves is (currently) unknown, unless another answer solves the issue.

The script

#!/usr/bin/env python3
import subprocess

# get the resolution of the screen (+0+0)
res = [
    int(n) for n in [
        s.split("+")[0].split("x")\
        for s in subprocess.check_output(["xrandr"]).decode("utf-8").split()\
        if "+0+0" in s][0]
    ]
# create list of windows
w_list = [w.split() for w in subprocess.check_output(["wmctrl", "-lG"]).decode("utf-8").splitlines()]
# filter only "normal" ones
valid = [
    w for w in w_list if "_NET_WM_WINDOW_TYPE_NORMAL" in\
    subprocess.check_output(["xprop", "-id", w[0]]).decode("utf-8")
    ]
# get the number of valid windows and calculate a targeted position
# the targeted position is a hunch, it will be exact if the window fits completely inside the resolution
# it will work anyway
parts = len(valid)+2
positions = [(int(n*res[0]/parts), int(n*res[1]/parts)) for n in list(range(parts))[1:-1]]
# unmaximaize, move the windows to the visible area (current workspace)
for i, w in enumerate(valid):
    subprocess.Popen(["wmctrl", "-ir", w[0], "-b", "remove,maximized_vert,remove,maximized_horz"])
    # weird, but sometimes wmctrl refuses to move a window if it is not resized first (?)
    subprocess.Popen(["wmctrl", "-ir", w[0], "-e", "0,200,200,200,200"])      
    subprocess.Popen(["wmctrl", "-ir", w[0], "-e", (",").join(["0", str(positions[i][0]), str(positions[i][1]),w[4],w[5]])])

How to use

  1. The script needs wmctrl:

    sudo apt-get install wmctrl
    
  2. Copy the script into an empty file, safe it as move_windows.py

  3. Test- run it: open a number of windows, place them on different workspaces etc., or try disconnecting the second monitor. Then run the command:

    python3 /path/to/move_windows.py
    

    All "normal" windows should move to the visible area of the current workspace.

  4. If all works fine, add it to a shortcut key: choose: System Settings > "Keyboard" > "Shortcuts" > "Custom Shortcuts". Click the "+" and add the command:

    python3 /path/to/move_windows.py
    

Now you should be able to move all windows into the visible area on the current workspace, with your shortcut key.