通过监视器旋转焦点

我的开发机器上有3台显示器,运行Ubuntu + Unity。 我通常将一台显示器专用于Web浏览和IM,另外两台用于运行vim或输入命令的终端。 我正在寻找一个单键序列来随意切换它们。

我想知道如何将焦点切换到每个监视器上的(顶部)窗口。 我正在寻找的行为或者类似于ALT + TAB的行为,除了在应用程序之间轮换(仅在旋转中使用最近最常用的应用程序实例),我可以在监视器之间旋转焦点。

作为妥协,我可以使用ALT + TAB机制,如果我可以拥有列表中的每个窗口。 不过,我仍然觉得这很烦人。

脚本通过监视器旋转焦点

在下面的设置中,涉及两个脚本:一个后台脚本,用于跟踪聚焦窗口的历史记录(请参阅底部的说明以了解需要的原因),以及一个放置在快捷键下的“操作”脚本,将焦点设置在下一个屏幕上。 如果下一个屏幕当前没有设置焦点的窗口,则会显示一条消息:

在此处输入图像描述

脚本

脚本1; 后台脚本,将其(确切地)保存为focus_track.py

 #!/usr/bin/env python3 import subprocess import time import os rootdata = os.environ["HOME"]+"/.focus_history" def get_screendata(): return sorted([int(s.split("+")[-2]) for s in subprocess.check_output(["xrandr"]).decode("utf-8").split() if s.count("+") == 2]) def current_windows(): try: return subprocess.check_output(["wmctrl", "-lG"]).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): 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] if __name__ == "__main__": open(rootdata, "wt").write("This is an empty line") 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 top == oldlist[0]: # 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; 动作脚本,将其另存为next_focus.py 在与 脚本1 相同的目录

 #!/usr/bin/env python3 import subprocess import focus_track # read existing windows and their order (x-wise) from file windows = [w.split() for w in focus_track.read_data()] w_data = [[w[0], int(w[2])] for w in windows] # get position of currently focussed window currfocus = focus_track.get_top(focus_track.current_windows().splitlines()).split()[2] # get screendata screens = focus_track.get_screendata() def screen_pos(x): return [(int(x) > n) for n in screens].count(True) scr_position = screen_pos(currfocus) next_screen = 1 if scr_position == len(screens) else scr_position+1 try: next_focus = [w for w in w_data if screen_pos(w[1]) == next_screen][0] subprocess.Popen(["wmctrl", "-ia", next_focus[0]]) except IndexError: subprocess.Popen(["notify-send", "No window to focus on next screen"]) 

如何使用

  1. 脚本需要安装wmctrl

     sudo apt-get install wmctrl 
  2. 将脚本1复制到空文件中,将其保存为(完全) focus_track.py 。 这个名称很重要,因为两个脚本共享function; 脚本1 导入脚本2。
  3. 将脚本2复制到一个空文件中,将其保存为(完全) next_focus.py ,并将其保存在与脚本1相同的目录中
  4. 测试运行设置: NB在打开(并因此聚焦)窗口之前启动后台脚本。 在后台脚本启动之前打开的Windows不会被“记录”,直到它们被聚焦

    • 使用以下命令启动后台脚本(例如从终端):

       python3 /path/to/focus_track.py 
    • 在不同的屏幕上,打开窗户。

    • 使用以下命令运行脚本2:

       python3 /path/to/next_focus.py 

    焦点应切换到下一个屏幕。 如果当前屏幕是行中的最后一个屏幕,焦点将切换到第一个屏幕。

  5. 如果一切正常,请将脚本1添加到启动应用程序:Dash>启动应用程序>添加命令:

     python3 /path/to/focus_track.py 

    并将脚本2添加到键盘快捷方式:选择:系统设置>“键盘”>“快捷方式”>“自定义快捷方式”。 单击“+”并添加命令:

     python3 /path/to/next_focus.py 

    到你喜欢的快捷键。

笔记

  • 由于后台脚本会跟踪焦点历史记录 ,为了立即正常运行,最好开始使用计算机之前启动它,例如在登录时(使用启动应用程序)。
  • 该脚本假定屏幕从左到右设置,不重叠。 然而,它们的垂直对齐无关紧要。
  • 为了“节省燃料”,并使脚本的负载可忽略不计,脚本每秒更新一次焦点历史记录(仅限)。 因此,如果窗口具有至少 0.5到1秒的焦点,则窗口被定义为聚焦
  • 虽然我在双屏幕设置上进行了测试,但它应该(并且我很确定它确实)在具有3(+)屏幕的设置上正常工作。


说明:

需要什么

要将焦点从一个屏幕切换到另一个屏幕,有必要确定每个屏幕的最前面窗口。 然而,主要问题是遍布多个屏幕的窗口实际上都在同一个堆栈中,因此在一个相同的连续中排序(z-)。 在最好的情况下,我们拥有的工具( wmctrlxdotoolxprop等)只能确定当前活动的窗口。 由于窗口位于活动窗口下方,因此它们不会向我们提供有关其他屏幕上的窗口顺序的任何信息。

在此处输入图像描述

因此,乍一看,将焦点从一个屏幕切换到另一个屏幕似乎是不可能的。
然而:

如何获取信息

然而,通过解决方法,有一个转义:如果我们使后台脚本跟踪当前聚焦的窗口,并保持更改的历史记录(只要窗口存在),我们实际上可以得出什么是z-当前打开的窗口的顺序。 如果我们还跟踪它们的几何形状和位置,我们就能获得所需的所有信息。

一个例子:
我们目前有五个窗口:A,B,C,D,E。如果它们的焦点通过D,E,A,C,B变化,我们知道窗口的z顺序是:B,C,A,E, D(从前到后)

连同它们的位置(x方向)和屏幕数据(屏幕的x分辨率),我们拥有所需的所有信息。 要将焦点切换到下一个屏幕,我们只需要查找位于下一个屏幕上的第一个窗口。