What you ask is actually to allow a specific application's window to only appear either on first- or last position, z-wise.
When the gedit window (in this example) loses focus, it is sent to the last poition (z-wise, below the semi-transparent terminal window) instead of descending only one position:
Z- position of a window
While it can be done, we still have to overcome some serious complications; When the window is sent to the very last position, you will want to keep the z-order of all other windows. However, currently, there are no tools that can give us this z-order of windows. Both xdotool
and wmctrl
give us no information on this whatsoever.
What we can do however is to keep track of the focus history of (all) windows. Since a window descends one position if another window gets focus, we can conclude the z-order of windows if we run a background script to watch the focus history of windows.
The solution two small background scripts
The solution below exists of two small background scripts, to run simultaneously.
- A script to keep track of the focus history:
focus_history.py
- A script to send the targeted application's window to the last position if it loses focus:
set_z.py
Script 1
focus-history.py
#!/usr/bin/env python3
import subprocess
import time
import os
rootdata = os.environ["HOME"]+"/.focus_history"
open(rootdata, "wt").write("This is an empty line")
def current_windows():
try:
return subprocess.check_output(["wmctrl", "-lp"]).decode("utf-8")
except subprocess.CalledProcessError:
pass
def convert_format(w_id):
return w_id[:2]+(10-len(w_id))*"0"+w_id[2:]
def read_data():
return open(rootdata).read().splitlines()
def get_top(wlist):
try:
top = convert_format(
[l.split("#")[-1].strip() for l in subprocess.check_output(
["xprop", "-root"]
).decode("utf-8").splitlines() \
if "_NET_ACTIVE_WINDOW(WINDOW)" in l][0])
return [l for l in wlist if top in l][0]
except IndexError:
pass
if __name__ == "__main__":
while True:
time.sleep(1)
wdata = current_windows()
if wdata != None:
wlist = wdata.splitlines()
# get frontmost window (as in wmctrl -lG)
top = get_top(wlist)
oldlist = read_data()
if not any([top == oldlist[0], top == None]):
# clean up closed windows
[oldlist.remove(l) for l in oldlist if not l.split()[0] in wdata]
# remove possible other mentions of the active window
[oldlist.remove(l) for l in oldlist if l.startswith(top.split()[0])]
open(rootdata, "wt").write(("\n").join([top]+oldlist))
Script 2
#!/usr/bin/python3
import subprocess
import time
import focus_history
# --- set the process name of your application below
proc = "gedit"
# ---
focus_hist = focus_history.rootdata
def get(val):
try:
return subprocess.check_output(val).decode("utf-8").strip()
except subprocess.CalledProcessError:
pass
def front_w():
get_front = str(hex(int(get(["xdotool", "getactivewindow"]))))
return get_front[:2]+(10-len(get_front))*"0"+get_front[2:]
while True:
time.sleep(1)
pid = get(["pgrep", proc])
front1 = ""
while pid:
time.sleep(1)
frontpid = get(["xdotool", "getactivewindow", "getwindowpid"])
front2 = frontpid == pid
if front2 != front1:
if front2 == False:
zdata = [l for l in open(focus_hist).read().splitlines()]
wins = list(reversed([l.split()[0] for l in zdata if not pid in l]))
for w in wins+[front_w()]:
cmd = ["xdotool", "windowraise", w]
subprocess.call(cmd)
pid = get(["pgrep", proc])
front1 = front2
How to set up
The script uses both wmctrl
and xdotool
sudo apt-get install wmctrl xdotool
Copy script 1 into an empty file, save it (exactly!) as focus_history.py
Copy script 2 into an empty file, save it as set_z.py
in the exact same directory as script 1.
In the head section of the script, in the line:
proc = "gedit"
replace "gedit"
by the process name of your application (between quotes)
Test- run the script: Before opening any (additional) windows, start script 1 by the command:
python3 /path/to/focus_history.py & python3 /path/to/set_z.py
[The script will recognize windows that were focussed at least once. That will be the case if the script is run on log in]
As mentioned, the scripts should be in one and the same directory, on the same level.
Now start opening windows and see how it behaves. Your application should move to the (very) background if it loses focus.
If all works fine, add it to Startup Applications: Dash > Startup Applications > Add. Add the command:
/bin/bash -c "sleep 15 && python3 /path/to/focus_history.py & python3 /path/to/set_z.py"
Notes
- the setup assumes you have a single window open of the targeted application. From your question, I understand that is the case.
Alternatively
Alternatively, you could set a shortcut key to raise a specific applications's window if it exists, as explained here.
That would require however to have another shortcut to go back to the first application's window,
Unless...,
You would setup one shortcut to toggle between two applications. That would however be out of the scope of this question...
Best Answer
Why this is not possible
What you ask, is essentially impossible. To make the explanation simple, there are two types of graphical desktops: stacking and tiling. Unity is an example of stacking desktop, where you have a stack of windows ( kind of like cafeteria tray stack ) , where the currently active window is the top one.
The mouse behavior has been defined in X11 such that clicking on a window brings it to the top of the stack. While there are ways to keep window below others (most notably with
wmctrl
), there is no way to click through a window without raising it into focus. This is not up to tweaking unfortunately - unless you are willing to create some form of new layer for GUI yourself, and so far I've not heard of any such project.If you request so, I can even place bounty on this question, but I can bet there will be no answer which satisfies your exact requirements.
Somewhat of a workaround
One can do this: set transparent terminal window to minimum size, set it as "always on top" and click on *anything around the window itself.
In this screenshot you can see two approaches to this : one via default right click on window border and via
wmctrl
command. The last one can allow for scripting approach to this , but the basic premise is the same - you have to click on anything around teminal itself.Possible alternatives
Guake
. You can have a drop down terminal linked to F12 key, with window that retains position on top, and you can click on anything around itGuake example