Move windows to specific screens using the command line
Solution 1
Moving all windows of a specific window class to a specific screen by (screen-) name
The script below will send windows, belonging to a specific WM_CLASS
(application), to a specific screen, by the screen's name. How that is done is explained in the script and also further below.
The script assumes the screens are arranged horizontally, and more or less top- aligned (with a difference < 100 PX).
The script
#!/usr/bin/env python3
import subprocess
import sys
# just a helper function, to reduce the amount of code
get = lambda cmd: subprocess.check_output(cmd).decode("utf-8")
# get the data on all currently connected screens, their x-resolution
screendata = [l.split() for l in get(["xrandr"]).splitlines() if " connected" in l]
screendata = sum([[(w[0], s.split("+")[-2]) for s in w if s.count("+") == 2] for w in screendata], [])
def get_class(classname):
# function to get all windows that belong to a specific window class (application)
w_list = [l.split()[0] for l in get(["wmctrl", "-l"]).splitlines()]
return [w for w in w_list if classname in get(["xprop", "-id", w])]
scr = sys.argv[2]
try:
# determine the left position of the targeted screen (x)
pos = [sc for sc in screendata if sc[0] == scr][0]
except IndexError:
# warning if the screen's name is incorrect (does not exist)
print(scr, "does not exist. Check the screen name")
else:
for w in get_class(sys.argv[1]):
# first move and resize the window, to make sure it fits completely inside the targeted screen
# else the next command will fail...
subprocess.Popen(["wmctrl", "-ir", w, "-e", "0,"+str(int(pos[1])+100)+",100,300,300"])
# maximize the window on its new screen
subprocess.Popen(["xdotool", "windowsize", "-sync", w, "100%", "100%"])
How to use
-
The script needs both
wmctrl
andxdotool
:sudo apt-get install xdotool wmctrl
Copy the script below into an empty file, save it as
move_wclass.py
-
Run it by the command:
python3 /path/to/move_wclass.py <WM_CLASS> <targeted_screen>
for example:
python3 /path/to/move_wclass.py gnome-terminal VGA-1
For the WM_CLASS
, you may use part of the WM_CLASS
, like in the example. The screen's name needs to be the exact and complete name.
How it is done (the concept)
The explanation is mostly on the concept, not so much on the coding.
In the output of xrandr, for every connected screen, there is a string/line, looking like:
VGA-1 connected 1280x1024+1680+0
This line gives us information on the screen's position and its name, as explained here.
The script lists the information for all screens. When the script is run with the screen and the window class as arguments, it looks up the (x-) position of the screen, looks up all windows (-id's) of a certain class (with the help of wmctrl -l
and the output of xprop -id <window_id>
.
Subsequently, the script moves all windows, one by one, to a position on the targeted screen (using wmctrl -ir <window_id> -e 0,<x>,<y>,<width>,<height>
) and maximizes it (with xdotool windowsize 100% 100%
).
Note
The script worked fine on the tests I ran it with. Using wmctrl
, and even xdotool
, on Unity can have some stubborn peculiarities however that sometimes need to be solved by experiment rather than reasoning. If you might run into exceptions, please mention.
Solution 2
I've rewrited @jacobs python code to simple bash and make it works (I tested this on ubuntu 16 cinnamon).
I had to add remove,maximized_vert, remove,maximized_horz
without that windows didn't move.
#!/bin/bash
if [ ! -z "$1" ] || [ -z "$2" ]; then
command=$(wmctrl -l | grep $1 | cut -d" " -f1)
if [ ! -z "$command" ]; then
position=$(xrandr | grep "^$2" | cut -d"+" -f2)
if [ ! -z "$position" ]; then
for window in $command; do
wmctrl -ir $window -b remove,maximized_vert
wmctrl -ir $window -b remove,maximized_horz
wmctrl -ir $window -e 0,$position,0,1920,1080
wmctrl -ir $window -b add,maximized_vert
wmctrl -ir $window -b add,maximized_horz
done
else
echo -e "not found monitor with given name"
fi
else
echo -e "not found windows with given name"
fi
else
echo -e "specify window and monitor name;\nmove.sh window-name monitor-name"
fi
sudo apt-get install xdotool wmctrl
/path/to/script.sh "window-name" "monitor-name"
Solution 3
For the record, here is what I use for the combination of this question and Restore multiple monitor settings:
# configure multiple displays and
# move the windows to their appropriate displays
import subprocess
import os
import wmctrl
import re
mydisplays = [("VGA1",0,"left"),
("eDP1",1080,"normal"),
("HDMI1",3000,"left")]
# https://askubuntu.com/questions/702002/restore-multiple-monitor-settings
def set_displays ():
subprocess.check_call(" && ".join([
"xrandr --output %s --pos %dx0 --rotate %s" % d for d in mydisplays]),
shell=True)
# https://askubuntu.com/questions/702071/move-windows-to-specific-screens-using-the-command-line
mywindows = [("/emacs$","VGA1"),
("/chrome$","HDMI1"),
("gnome-terminal","eDP1")]
def max_windows ():
didi = dict([(d,x) for d,x,_ in mydisplays])
for w in wmctrl.Window.list():
try:
exe = os.readlink("/proc/%d/exe" % (w.pid))
for (r,d) in mywindows:
if re.search(r,exe):
x = didi[d]
print "%s(%s) --> %s (%d)" % (r,exe,d,x)
w.set_properties(("remove","maximized_vert","maximized_horz"))
w.resize_and_move(x,0,w.w,w.h)
w.set_properties(("add","maximized_vert","maximized_horz"))
break
except OSError:
continue
def cmdlines (cmd):
return subprocess.check_output(cmd).splitlines()
def show_displays ():
for l in cmdlines(["xrandr"]):
if " connected " in l:
print l
if __name__ == '__main__':
show_displays()
set_displays()
show_displays()
max_windows()
you would need to use wmctrl version 0.3 or later (because of my pull request).
Related videos on Youtube
sds
Updated on September 18, 2022Comments
-
sds over 1 year
This is similar to Quickly place a window to another screen using only the keyboard , but I want to be able to use the command line (so that all I need to do is to recall the command line from the bash history).
E.g., send
- all the gnome terminal windows to
eDP1
, - all Emacs windows to
VGA1
, and - all Chrome windows to
HDMI1
(and maximize them after moving - but not the crazy F11 way, the normal window-manager-style maximization).
I would like to specify windows by the executable name.
- all the gnome terminal windows to
-
sds over 8 yearsI would use
wmctrl -b add,maximized_vert,maximized_horz
instead ofxdotool
. -
sds over 8 yearswhat is "i.c.w."?
-
Jacob Vlijm over 8 years@sds in combination with, might not be a usual expression in English :)
-
RoundSparrow hilltx almost 7 yearsThis code references a variable "scr" in the IndexError exception - but there is no such variable defined. Getting a NameError.
-
Jacob Vlijm almost 7 years@RoundSparrowhilltx sharp! must have been a change of plan during coding. fixed now.
-
RoundSparrow hilltx almost 7 years@JacobVlijm Thank you. The variable issue seems fixed. However, the script does not seem to function correctly on my Ubuntu 17.04 desktop system. All it seems to do is move the window slightly within the same physical screen.
python3 movetest.py Calculator HDMI-0
andpython3 movetest.py Calculator DP-0
only moves the Calculator window a hundred pixels left or right, not changing to the target screen. xrandr output is here: gist.github.com/RoundSparrow/60bed4d09f68e8c52db584474963b76e -
Adam Goldman almost 5 yearshaven't tested it yet, do you have a script to move all windows at once?
-
Adam Goldman almost 5 yearshaven't tested it yet, do you have a script to move all windows at once? That would be great :)
-
Andrzej Piszczek almost 5 years@AdamGoldman I named script as "move-window" and link it to
/usr/bin/move-window
then I created alias like that:alias mw='\ move-windows PhpStorm eDP-1;\ move-windows Sublime eDP-1;\ move-windows Chrome HDMI-1; ';
Then it moves three apps at once. -
Adam Goldman almost 5 yearsThank you for the answer man. For me this only focus the window, doesn't move it to the other monitor
-
Adam Goldman almost 5 yearsOh I see, there's a different problem, never mind :)
-
rob74 about 4 years@JacobVlijm: for me (Kubuntu 16.04),
wmctrl -b add,maximized_vert,maximized_horz
as suggested by @sds worked perfectly, while usingxdotool
maximized the window over all monitors (which is usually not what you want - especially in my case, where one of the monitors is in portrait mode, so maximizing a window over the whole desktop will leave parts of it invisible). As always, YMMV :) -
fimbulvetr over 3 yearsThanks for the bash version. My python loves to eat itself and die, packages broken and pip won't install. This is great. One minor edit I had was I needed the full window name for pycharm (or any idea IDE), so I changed the 4th line to ...
wmctrl -lx
... (added thex
) so now I can runmove-window jetbrains HDMI-1
-
Gregory Kelly over 3 yearsThank you for the script. I made two modifications. The first command argument
$1
should be enclosed in double quotes (grep "$1"
) otherwise it does not work for window names with multiple words. Furthermore adding a linewmctrl -ir $window -b remove,fullscreen
also enables moving also windows in fullscreen mode.