I run a Matlab
script in the workspace 1
. This generates several plots. In the mean time I switch to workspace 2
and working there. My problem is that the plots are popping up in workspace 2
. Is it possible to lock on software into a workspace.
So while Matlab
generates the plots in workspace 1
, I can work in workspace 2
without the disruption of the poping up plots?
Ubuntu – How to lock an application (and all its new windows) into a specific workspace
14.04MATLABunityworkspaces
Related Solutions
I don't think that there's such a function for the standard window manager.
But Devil's Pie can do what you want.
http://burtonini.com/blog/computers/devilspie/
Quote from Devil's Pie website: "Devil's Pie can be configured to detect windows as they are created, and match the window to a set of rules. If the window matches the rules, it can perform a series of actions on that window. For example, I can make all windows created by X-Chat appear on all workspaces, and the main Gkrellm1 window does not appear in the pager or task list."
I hope this helped you,
Daniel
It can be done very well, but you need some understanding on Unity/viewports. I hope the story below is understandable, if not, please leave a comment.
The script below can be used to open a window of any application on any of your viewports, on any position, if you run it with the right arguments. The script is an edited version of this one, but now prepared to place windows on the spanning virtual desktop.
1. Understanding viewports and window coordinates
Workspaces in Unity
In Unity, unlike other window managers, you actually only have one spanning workspace, which is divided into viewports. In your case, your workspace is divided into eight viewports.
How the position of the windows is defined
The window position, as the output of the command:
wmctrl -lG
(you need to have wmctrl installed to run the command)
is described as the position, relative to the upper left corner of the current viewport:
So if you are on viewport 1
:
a window on viewport 2 to could be positioned on e.g. 1700 (x-wise) x 500 (y-wise)
(my screen is 1680x1050)
However, if you are on viewport 6:
the same window would be positioned on 20 (x), -550 (y)
Using these coordinates correctly is important to run the script with the right arguments, as described below:
2. How to use the script
The script below can be used to place a new window of an application on your virtual (spanning) workspace.
Make sure
wmctrl
is installed:sudo apt-get install wmctrl
Copy the script below into an empty file, save it as
setwindow
(no extension) in~/bin
. Create the directory if it doesn't exist yet. Make the script executable.- If you just created
~/bin
, either run the commandsource ~/.profile
or log out/in to make the directory available in$PATH
. Test run the command:
setwindow <application> <x_position> <y_position> <horizontal_size> <vertical_size>
e.g.
setwindow gedit 100 100 200 200
A gedit window should show up on the current viewport.
Notes:
- Keep in mind that not all applications allow window sizes below a certain width or height. The minimum width of a
gedit
window on my system is e.g. appr. 470 px. - The script only works fine if the whole window fits on the targeted viewport, choose your coordinates/sizes accordingly. Also mind that the Unity Launcher and the panel use some space (!) which can influence the position of the window.
- Use negative
<x_position>
to place windows on the left of the current viewport(s) - Use negative
<y_position>
to place windows above the current viewport(s) To open new windows on different viewports at once, you can simply chain commands. Looking at the viewport setup in the "Long story" example, If I am on viewport 1, I can open gedit windows on viewport 1, 2, 3 and 4 with the command:
setwindow gedit 100 100 200 200&&setwindow gedit 1780 100 200 200&&setwindow gedit 3460 100 200 200&&setwindow gedit 5140 100 200 200
The script
#!/usr/bin/env python3
import subprocess
import time
import sys
app = sys.argv[1]
get = lambda x: subprocess.check_output(["/bin/bash", "-c", x]).decode("utf-8")
ws1 = get("wmctrl -lp"); t = 0
subprocess.Popen(["/bin/bash", "-c", app])
# 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:
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,"+sys.argv[2]+","+sys.argv[3]+","+sys.argv[4]+","+sys.argv[5]
for cmd in [cmd1, cmd2, cmd3]:
subprocess.call(["/bin/bash", "-c", cmd])
break
time.sleep(0.5)
t = t+1
EDIT: the lazy version
In case you'd prefer to just enter coordinates and size, simply as if you would open a window on the current viewport, and give the targeted viewport as an argument (without having to calculate anything), then use the version below...
If you set it up like the first version of the script, you can run it with the command:
setwindow <application> <x_position> <y_position> <horizontal_size> <vertical_size> <targeted_viewport>
An example: to open a Google-Chrome
window positioned on 20, 20
, size 300x300
, on viewport 5
:
setwindow google-chrome 20 20 300 300 5
The setup is pretty much the same as the first version of the script.
Note that also this script only works correctly if the defined window (position/size) fits completely within the targeted viewport.
The script:
#!/usr/bin/env python3
import subprocess
import time
import sys
app = sys.argv[1]
target_vp = int(sys.argv[6])
def get_res():
# get resolution
xr = subprocess.check_output(["xrandr"]).decode("utf-8").split()
pos = xr.index("current")
return [int(xr[pos+1]), int(xr[pos+3].replace(",", "") )]
res = get_res()
def current(set_vp):
# get the current viewport
vp_data = subprocess.check_output(
["wmctrl", "-d"]
).decode("utf-8").split()
dt = [int(n) for n in vp_data[3].split("x")]
cols = int(dt[0]/res[0])
rows = int(dt[1]/res[1])
curr_vpdata = [int(n) for n in vp_data[5].split(",")]
curr_col = int(curr_vpdata[0]/res[0])
curr_row = int(curr_vpdata[1]/res[1])
curr_vp = curr_col+curr_row*cols+1
# calculate the vector to the origin from the current viewport (in resolution units)
vec_curr = vector(curr_vp, cols)
# calculate the vector to the origin from the targeted viewport
vec_set = vector(set_vp, cols)
# calculate the vector between current and targeted viewport
vec_relative = [vec_set[0] - vec_curr[0],
vec_set[1] - vec_curr[1]]
# calculate needed correction (absolute)
relative = [vec_relative[0]*res[0],
vec_relative[1]*res[1]]
return relative
def vector(vp, cols):
rem = vp%cols
vec_x = rem-1 if rem != 0 else cols-1
vec_y = int((vp-1)/cols)
return [vec_x, vec_y]
res = get_res() # nieuw
get = lambda x: subprocess.check_output(["/bin/bash", "-c", x]).decode("utf-8")
ws1 = get("wmctrl -lp"); t = 0
# check for additional arguments to run the application
try:
subprocess.Popen(["/bin/bash", "-c", app+" "+sys.argv[7]])
except IndexError:
subprocess.Popen(["/bin/bash", "-c", app])
# 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:
w_id = procs[0][0][1]
cmd1 = "wmctrl -ir "+w_id+" -b remove,maximized_horz"
cmd2 = "wmctrl -ir "+w_id+" -b remove,maximized_vert"
# calculate the correction, related to the current workspace, marge for launcher and panel
pos_x = int(sys.argv[2]); pos_y = int(sys.argv[3]); x_marge = 65; y_marge = 35
pos_x = pos_x if pos_x > x_marge else x_marge; pos_y = pos_y if pos_y > y_marge else y_marge
x_relative = pos_x+current(target_vp)[0]
y_relative = pos_y+current(target_vp)[1]
# correct possible inaccurately set width / height
x_size = res[0]; y_size = res[1]
set_width = int(sys.argv[4]); set_height = int(sys.argv[5])
width = set_width if set_width+x_marge+pos_x < x_size else x_size - pos_x - x_marge
height = set_height if set_height+y_marge+pos_y < y_size else y_size - pos_y - y_marge
cmd3 = "wmctrl -ir "+w_id+" -e 0,"+str(x_relative)+","+str(y_relative)+","+str(width)+","+str(height)
for cmd in [cmd1, cmd2, cmd3]:
subprocess.call(["/bin/bash", "-c", cmd])
break
time.sleep(0.5)
t = t+1
Opening application windows with arguments
To finish the job, answering your question completely:
If you run the script as e.g.:
setwindow google-chrome 20 20 300 300 5
it will open a default window on the targeted desktop(s).
With the latest version of the script however, you can add an additional argument to open the application window, for example a url
:
setwindow <application> <x_position> <y_position> <horizontal_size> <vertical_size> <targeted_viewport> <(optional)_argument>
e.g.:
setwindow google-chrome 0 0 600 600 3 "--new-window http://askubuntu.com"
If the (extra) argument contains spaces, use quotes. The above example will open agoogle-chrome
window on viewport 3, opening the url
http://askubuntu.com
.
You can chain commands to open multiple windows/urls on different workspaces in one command, e.g.:
setwindow google-chrome 0 0 600 600 8 "--new-window http://askubuntu.com"&&setwindow google-chrome 0 0 600 600 7 "--new-window www.google.com"
Best Answer
IMPORTANT EDIT
Below a rewritten version of the script from the first answer (below). The differences:
WM_CLASS
and the targeted workspace are now arguments to run the script. Only use either the first or the second (identifying) part of theWM_CLASS
(see further below: how to use)When the script starts, it shows a notification (example
gedit
):The script
How to use
The script needs both
wmctrl
andxdotool
:Copy the script above into an empty file, save it as
lock_towspace.py
Of your specific application, find out the
WM_CLASS
: open your application, run in a terminal:The output will look like (in your case):
Either use the first or the second part in the command to run the script.
The command to run the script then is:
In the command, the last section;
2,2
is the workspace where you want to lock the application to (without spaces: (!) column, row), in "human" format; the first column/row is1,1
OUTDATED ANSWER:
(second) TEST VERSION
The script below locks a specific application to its initial workspace. If the script is started, it determines on which workspace the application resides. All additional windows the application produces will be moved to the same workspace in a split second.
The focus issue is solved by automatically re- focussing on the window that was focussed before the additional window was produced.
The script
How to use
The script needs both
wmctrl
andxdotool
Copy the script into an empty file, save it as
keep_workspace.py
determine your application's `WM_CLASS' by opening the application, then open a terminal and run the command:
Then click on your application's window. Copy the output, looking like
"sun-awt-X11-XFramePeer", "MATLAB R2015a - academic use"
in your case, and place it between single quotes in the head section of the script, as indicated.Run the script with the command:
If it works as you like, I'll add a toggle function. Although it works already for a few hours on my system, bu it might need some tweaking first however.
Notes
Although you should not notice it, the script does add some processor load to the system. On my elderly system I noticed an increase of 3-10%. If you like how it works, I will probably further tweak it to reduce the load.
The script, as it is, assumes the secundary windows are of the same class as the main window, like you indicated in a comment. With a (very) simple change, the secondary windows can be of another class however.
Explanation
Although probably not very interesting for an average reader, the script works by calculating in vectors. On startup, the script calculates:
wmctrl -d
wmctrl -lG
From then on, the script looks for new windows of the same application, with the output of
xprop WM_CLASS
, looks up their position in the same way as above and moves them to the "original" workspace.Since the newly created window "stole" the focus from the last used window the user was working on, the focus is subsequently set to the window that had focus before.