Ubuntu – Move windows between screens in dual screen mode

multiple-monitorsscreenwindow-manager

I have a laptop and a 27 inch monitor. I have Qt running on one monitor and Pycharm on the other. Is there a way to make a key combination to swap all the windows between both screens. The reason is that I want to program on the big screen only. I have already 4 workspaces and all of them are already used.

The output of xrandr:

Screen 0: minimum 320 x 200, current 3840 x 1080, maximum 32767 x 32767
eDP1 connected 1920x1080+1920+0 (normal left inverted right x axis y axis) 344mm x 193mm
   1920x1080      60.2*+   59.9  
   1680x1050      60.0     59.9  
   1600x1024      60.2  
   1400x1050      60.0  
   1280x1024      60.0  
   1440x900       59.9  
   1280x960       60.0  
   1360x768       59.8     60.0  
   1152x864       60.0  
   1024x768       60.0  
   800x600        60.3     56.2  
   640x480        59.9  
HDMI1 connected 1920x1080+0+0 (normal left inverted right x axis y axis) 597mm x 336mm
   1920x1080      60.0*+   50.0     59.9  
   1920x1080i     60.1     50.0     60.0  
   1600x1200      60.0  
   1680x1050      59.9  
   1280x1024      75.0     60.0  
   1440x900       59.9  
   1280x960       60.0  
   1366x768       59.8  
   1152x864       75.0  
   1280x720       60.0     50.0     59.9  
   1024x768       75.1     70.1     60.0  
   832x624        74.6  
   800x600        72.2     75.0     60.3     56.2  
   720x576        50.0  
   720x480        60.0     59.9  
   640x480        75.0     72.8     66.7     60.0     59.9  
   720x400        70.1  
DP1 disconnected (normal left inverted right x axis y axis)
HDMI2 disconnected (normal left inverted right x axis y axis)
VIRTUAL1 disconnected (normal left inverted right x axis y axis)

Best Answer

1. Script to swap all windows from screen 1 --> screen 2 and vice versa

The script assumes the screens are of the same vertical resolution, and the left screen is the primary one. The horizontal resolutions of both screens is searched by the script.

How to set up

The script needs wmctrl to be installed:

sudo apt-get install wmctrl
  1. Copy the script below into an empty file, save it as swap_windows (no extension) in ~/.bin. Create the directory if it doesn't exist already, and make the script executable.
  2. If you just created the directory ~/bin (it didn't exist yet), either log out/in or run in a terminal: source ~/.profile.
  3. test run the script with the command:

    swap_windows
    
  4. If all works as expected, add shortut key; choose: System Settings > "Keyboard" > "Shortcuts" > "Custom Shortcuts". Click the "+" and add the command

The script

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

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

def get_shiftright(xr_output):
    lines = [l for l in xr_output.splitlines() if "+0+0" in l][0].split()
    return int([it for it in lines if "x" in it][0].split("x")[0])

def get_shiftleft(xr_output):
    lines = [l for l in xr_output.splitlines() if  "+0" in l and not "+0+0" in l][0].split()
    return -int([it for it in lines if "x" in it][0].split("x")[0])

def swap_windows():
    xr_output = get("xrandr")
    shift_r = get_shiftright(xr_output)
    shift_l = get_shiftleft(xr_output)
    w_data = [l.split() for l in get("wmctrl -lG").splitlines()]
    for w in w_data:
        props = get("xprop -id "+w[0])
        if any(["_TYPE_NORMAL" in props, "TYPE_DIALOG" in props]):    
            if 0 < int(w[2]) < shift_r:
                shift = shift_r
            elif shift_r-shift_l > int(w[2]) >= shift_r:
                shift = -shift_r
            else:
                shift = 0
            command = "wmctrl -ir "+w[0]+" -e 0,"+(",").join([str(int(w[2])+shift), str(int(w[3])-28), w[4], w[5]])
            subprocess.Popen(["/bin/bash", "-c", command])     
swap_windows()



2. Script to move (all) windows from one monitor to the other

The script below moves windows in a dual monitor setup from one screen to another, either:

  • from the left to the right monitor -->

    or

  • from the right to the left monitor <--

Depending on the argument you run it with (left or right)

The script (again) assumes the screens are of the same vertical resolution, and the left screen is the primary one. The horizontal resolutions of both screens is searched by the script.

How to set up

The script needs wmctrl to be installed:

sudo apt-get install wmctrl
  1. Copy the script below into an empty file, save it as shift_windows (no extension) in ~/.bin. Create the directory if it doesn't exist already, and make the script executable.
  2. If you just created the directory ~/bin (it didn't exist yet), either log out/in or run in a terminal: source ~/.profile.
  3. test run the script with the commands

    shift_windows right
    

    and: shift_windows left

    In the first case, windows on your left screen should move to the right screen and in the second case vice versa.

  4. If all works as expected, add the script to two shortcut combinations: choose: System Settings > "Keyboard" > "Shortcuts" > "Custom Shortcuts". Click the "+" and add the commands as explained above.

The script

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

vec = sys.argv[1]

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

def get_shiftright(xr_output):
    lines = [l for l in xr_output.splitlines() if "+0+0" in l][0].split()
    return int([it for it in lines if "x" in it][0].split("x")[0])

def get_shiftleft(xr_output):
    lines = [l for l in xr_output.splitlines() if  "+0" in l and not "+0+0" in l][0].split()
    return -int([it for it in lines if "x" in it][0].split("x")[0])

def shift_windows():
    xr_output = get("xrandr")
    shift_r = get_shiftright(xr_output)
    shift_l = get_shiftleft(xr_output)
    w_data = [l.split() for l in get("wmctrl -lG").splitlines()]
    for w in w_data:
        props = get("xprop -id "+w[0])
        if vec == "right":
            check = (0 < int(w[2]) < shift_r, "_TYPE_NORMAL" in props, "TYPE_DIALOG" in props).count(True)
            shift = shift_r
        if vec == "left":
            check = (shift_r-shift_l > int(w[2]) >= shift_r , "_TYPE_NORMAL" in props, "TYPE_DIALOG" in props).count(True)
            shift = -shift_r
        if check == 2:
            command = "wmctrl -ir "+w[0]+" -e 0,"+(",").join([str(int(w[2])+shift), str(int(w[3])-28), w[4], w[5]])
            subprocess.Popen(["/bin/bash", "-c", command])
shift_windows()




3. Move a single window from one screen to another

Although not literally your question, with just a few lines more, you can either move all windows from one screen to the other, but also a single one (the frontmost) with a key combination.

With the script below, you can move all windows with the command:

shift_windows right

or move a single window with the command:

shift_windows right s

The setup is pretty much the same as the script above (don't forget to install wmctrl)

The script

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

vec = sys.argv[1]
try:
    n = sys.argv[2]
except IndexError:
    n = "a"

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

def get_shiftright(xr_output):
    lines = [l for l in xr_output.splitlines() if "+0+0" in l][0].split()
    return int([it for it in lines if "x" in it][0].split("x")[0])

def get_shiftleft(xr_output):
    lines = [l for l in xr_output.splitlines() if  "+0" in l and not "+0+0" in l][0].split()
    return -int([it for it in lines if "x" in it][0].split("x")[0])

def shift_windows():
    xr_output = get("xrandr")
    shift_r = get_shiftright(xr_output)
    shift_l = get_shiftleft(xr_output)
    w_data = [l.split() for l in get("wmctrl -lG").splitlines()]
    if n == "s":
        frontmost = [l for l in get("xprop -root").splitlines() if "_NET_ACTIVE_WINDOW(WINDOW)" in l][0].split()[-1]
        frontmost = frontmost[:2]+"0"+frontmost[2:]
        w_data = [l for l in w_data if frontmost in l]
    for w in w_data:
        props = get("xprop -id "+w[0])
        if vec == "right":
            check = (0 < int(w[2]) < shift_r, "_TYPE_NORMAL" in props, "TYPE_DIALOG" in props).count(True)
            shift = shift_r
        if vec == "left":
            check = (shift_r-shift_l > int(w[2]) >= shift_r , "_TYPE_NORMAL" in props, "TYPE_DIALOG" in props).count(True)
            shift = -shift_r
        if check == 2:
            command = "wmctrl -ir "+w[0]+" -e 0,"+(",").join([str(int(w[2])+shift), str(int(w[3])-28), w[4], w[5]])
            subprocess.Popen(["/bin/bash", "-c", command])
shift_windows()