我的大部分工作都是在两个应用程序中完成的:我的网络浏览器和我的编辑器。我经常用 Alt-Tab 在它们之间切换。我还一直打开一个 IM 客户端 (Hipchat),但与其他两个应用程序相比,我很少与它互动。
一个反复出现的烦恼是,在我与 Hipchat 窗口交互并按 Alt-Tab 返回到(比如说)我的编辑器后,我的肌肉记忆被调整为期望另一个 Alt-Tab 聚焦于我的浏览器,但我最终又回到了 Hipchat。
在 Hipchat 失去焦点后,有没有什么方法可以让它被发送到堆栈底部或最近列表或任何其他位置?
答案1
你要求的实际上是允许特定应用程序的窗口仅有的沿着 z 轴方向,出现在第一个位置或者最后一个位置。
当 gedit 窗口(在此示例中)失去焦点时,它将被发送到最后一个位置(z 方向,位于半透明终端窗口下方),而不是仅下降一个位置:
窗口的 Z 位置
虽然可以做到,但我们仍需克服一些严重的问题;当窗口被发送到最后位置时,您需要保留所有其他窗口的 Z 顺序。但是,目前没有工具可以为我们提供此窗口的 Z 顺序。两者都xdotool
没有wmctrl
提供任何相关信息。
然而,我们可以做的是跟踪(所有)窗口的焦点历史。由于如果另一个窗口获得焦点,则窗口会下降一个位置,因此我们可以得出结论如果我们运行后台脚本来观察窗口的焦点历史,则窗口的 z 顺序。
解决方案两个小后台脚本
下面的解决方案存在两个小型后台脚本,可同时运行。
- 跟踪焦点历史的脚本:
focus_history.py
- 如果目标应用程序的窗口失去焦点,则将其发送到最后一个位置的脚本:
set_z.py
脚本 1
焦点历史.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))
脚本 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
如何设置
脚本同时使用
wmctrl
和xdotool
sudo apt-get install wmctrl xdotool
将脚本 1 复制到一个空文件中,并将其保存为(准确无误!)
focus_history.py
将脚本2复制到一个空文件中,另存为
set_z.py
在同一目录中如脚本1所示。在脚本的头部部分,有以下行:
proc = "gedit"
替换
"gedit"
为你的应用程序的进程名称(引号之间)测试运行脚本:在打开任何(其他)窗口之前,通过命令启动脚本1:
python3 /path/to/focus_history.py & python3 /path/to/set_z.py
[脚本将识别至少获得一次焦点的窗口。如果脚本在登录时运行,情况就会如此]
如上所述,脚本应该位于同一个目录中,处于同一级别。
现在开始打开窗口并查看其行为。如果失去焦点,您的应用程序应该会移至(非常)后台。
如果一切正常,请将其添加到启动应用程序:Dash > 启动应用程序 > 添加。添加命令:
/bin/bash -c "sleep 15 && python3 /path/to/focus_history.py & python3 /path/to/set_z.py"
笔记
- 该设置假设你有一个单身的打开目标应用程序的窗口。从你的问题中,我了解到情况确实如此。
或者
或者,你可以设置快捷键来调出特定应用程序的窗口(如果存在),如下所述这里。
然而,这需要另一个快捷方式才能返回到第一个应用程序的窗口,
除非...,
您需要设置一个快捷方式来在两个应用程序之间切换。但这超出了本问题的讨论范围...