通过监视器旋转焦点

通过监视器旋转焦点

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

我想知道如何将焦点切换到每个显示器上的(顶部)窗口。我正在寻找的行为有点像 ALT+TAB,除了在应用程序之间旋转(这只会使最近使用的应用程序实例在旋转中可用)之外,我可以在显示器之间旋转焦点。

作为折衷,如果我可以将每个窗口都包含在列表中,我可以忍受使用 ALT+TAB 机制。不过,我仍然认为这会很烦人。

答案1

通过监视器旋转焦点的脚本

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

在此处输入图片描述

脚本

脚本 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 复制到一个空文件中,将其另存为 (exactly) focus_track.py。名称很重要,因为两个脚本共享功能;脚本 1 是进口进入脚本 2。
  3. 将脚本 2 复制到一个空文件中,保存为(准确)next_focus.py 与脚本 1 位于同一目录中
  4. 测试运行设置:注意:在打开(并因此聚焦)窗口之前启动后台脚本。在后台脚本启动之前打开的窗口在获得焦点之前不会被“记录”

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

      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 分辨率),我们就拥有了所需的所有信息。要将焦点切换到下一个屏幕,我们只需查找下一个屏幕上的第一个窗口即可。

相关内容