每个工作区的屏幕分辨率不同吗?

我在我的ubuntu笔记本上使用i3-wm。我的屏幕尺寸是3200x1800,对某些事情来说很好,但对其他事情来说很糟糕(Netflix和gvim的比较)。
我想要改变一个或多个工作区的分辨率,但我不知道是否可能...可以使用compton来做这个吗?或者也许还有其他程序可以实现吗?

你可以根据当前的视口或工作区来改变分辨率。然而,由于你只有一个屏幕,所以改变后的分辨率仍将是当前的分辨率。这对你有帮助吗? - Jacob Vlijm
理想情况下,我希望在3200x1800的屏幕上有一个工作区1,而在1920x1080的屏幕上有一个工作区2。并且能够在不通过任何命令或文件改变分辨率的情况下在这两个工作区之间切换。如果你是指这个的话。 - Fetts Vett
2这可能是“有点儿”可能的,意思是更改将会自动进行。然而,在Unity中,所有工作区(视口)一起构成一个大桌面,并且只有一个分辨率设置。可以做的是让一个后台脚本理解当前的工作区并相应地改变分辨率。这可能会对不同视口上的窗口排列造成混乱。不确定这对您是否有帮助。 - Jacob Vlijm
实际上,我认为这正是我想要的。由于i3是平铺式的,所以一切都应该保持相同的排列等等。在这个脚本中需要做些什么呢? - Fetts Vett
1机制将类似于此 http://askubuntu.com/a/560734/72216,只是不是更改壁纸,而是通过 xrandr 更改分辨率。我可以研究一下,但您能告诉我您的屏幕名称(从 xrandr 中查找“已连接”),要在哪个视口上设置分辨率以及分辨率的数量吗? (不确定是否需要) - Jacob Vlijm
太棒了,谢谢。是的,我的屏幕名称是"eDP1",工作区有1-10个,其中8、9、10号工作区设置为1920x1080,1-7号工作区设置为3200x1800,这就是我想要的。 - Fetts Vett
2个回答

1. 假设您正在使用Unity

下面的脚本应该根据Unity中的当前视口更改您的分辨率。我在几台计算机上测试了一段时间,它没有出现任何错误。

不过,我建议您也要测试一段时间,看看它是否能够连续运行而没有任何中断;重复的wmctrl命令有时可能会意外退出“非零”。如果是这样,我们需要建立一个try / except

注释

  • 我在单显示器设置上进行了测试。多个显示器可能需要另一种解析xrandr输出的方法。
  • 在脚本的头部,您需要为每个视口设置所需的分辨率,我按照您的评论中提到的方式进行了设置,但您可以随时更改它。使用以下格式:

    resolutions = [[<水平>, <垂直>], [<水平>, <垂直>], ......]
    

    就像脚本中显示的那样。

  • 不需要说您应该使用支持的分辨率,就像xrandr对于您的显示器的输出一样。

如何使用

1. 脚本需要 wmctrl
sudo apt-get install wmctrl
2. 将下面的脚本复制到一个空文件中,保存为 screen_res.py 3. 在终端窗口中使用以下命令进行测试运行:
python3 screen_res.py
4. 如果一切正常,请将其添加到启动应用程序中:Dash > 启动应用程序 > 添加...

剧本

#!/usr/bin/env python3
import subprocess
import time

# list of resolution per viewport, for each viewport a separate [hor, vert]
# I pre- entered your viewports. quite some! listing takes more space than the script :)
resolutions = [
    [3200, 1800],
    [3200, 1800],
    [3200, 1800],
    [3200, 1800],
    [3200, 1800],
    [3200, 1800],
    [3200, 1800],
    [1920, 1080],
    [1920, 1080],
    [1920, 1080],
    ]

def get_xr():
    return subprocess.check_output(["xrandr"]).decode("utf-8").split()

check = get_xr()
plus = 2 if check[check.index("connected")+1] == "primary" else 1

while True:
    # resolution:
    xr = get_xr()    
    res = [int(n) for n in xr[xr.index("connected")+plus].split("+")[0].split("x")]
    # get 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])
    curr_vpdata = [int(n) for n in vp_data[5].split(",")]
    curr_col = int(curr_vpdata[0]/res[0])+1; curr_row = int(curr_vpdata[1]/res[1])
    curr_vp = curr_col+curr_row*cols
    # check and change resolution if needed
    if res != resolutions[curr_vp-1]:
        new_res = ("x").join([str(n) for n in resolutions[curr_vp-1]])
        subprocess.call(["xrandr", "-s", new_res])
    time.sleep(1)

解释

在循环中,脚本首先从命令中检查屏幕的当前分辨率:
xrandr
随后,脚本从命令中检查总桌面大小(所有视口):
wmctrl -d
然后,从分辨率和总桌面大小计算列数,即总桌面大小除以水平分辨率。 wmctrl -d的输出中还包括跨越桌面的当前视口位置,例如:VP: 1680,0。 有了这些信息,我们可以确定所在的列和行,并检查当前设置的分辨率是否与脚本头部列表中定义的分辨率相匹配。如果不匹配,则脚本会使用以下命令更改分辨率:
xrandr -s <xres>x<yres>

2. XFCE版本

  • 设置与上述版本相同(还需要wmctrl
#!/usr/bin/env python3
import subprocess
import time

# list of resolution per viewport, for each viewport a separate [hor, vert]
# below just an example! set resolutions for your own screen
resolutions = [
    [1280, 1024],
    [1280, 960],
    [1280, 1024],
    [1280, 1024],
    ]

def get_xr():
    return subprocess.check_output(["xrandr"]).decode("utf-8").split()

check = get_xr()
plus = 2 if check[check.index("connected")+1] == "primary" else 1

while True:
    # resolution:
    xr = get_xr()    
    res = [int(n) for n in xr[xr.index("connected")+plus].split("+")[0].split("x")]
    # get current workspace
    vp_data = subprocess.check_output(["wmctrl", "-d"]).decode("utf-8").splitlines()
    curr_ws = int([l.split()[0] for l in vp_data if "*" in l][0])
    # check and change resolution if needed
    if res != resolutions[curr_ws]:
        new_res = ("x").join([str(n) for n in resolutions[curr_ws]])
        subprocess.call(["xrandr", "-s", new_res])
    time.sleep(1)

我看到你在一个无限循环中运行你的代码。难道没有一些事件可以监听或者一些钩子可以在桌面改变时触发吗? - umpirsky
@umpirsky一直在寻找,但从未找到过。同时,每秒运行一次的xrandrwmctrl -d应该相对轻量级。即使在我这台12年前的老古董上(XFCE版本),我也没有注意到任何影响。也许两者之间加一个小的间隔,比如0.5秒,会使它更加轻巧。 - Jacob Vlijm

我实施了@Jacob Vlijm的XFCE Python脚本。它运行良好,除了通过Wine运行的一个旧的Windows程序。每次脚本循环时,鼠标光标会冻结不到一秒钟。每秒检查1次意味着很多卡顿。我将其改写为Bash脚本,并修改为每次检查只调用一次wmctrl,并且只在需要更改分辨率时调用xrandr。请注意,这需要安装wmctrl。
#!/usr/bin/env bash
#Tested in XFCE 4.12.3 on Ubuntu 18.04
#Requires wmctrl. Install with:
#$ sudo apt-get install wmctrl

#Note: This checks current workspace res 1x/sec. To change rate, change "sleep" parameter below

#Enter appropriate resolution for each workspace, in order, in the same format as below.
RESOLUTIONS=('1920x1080' '1368x768')

check_and_change_res() {
  #echo "Parsing wmctrl -d"
  while read line; do
    #example line from wmctrl:
    #$ wmctrl -d
    #0  - DG: 1368x768  VP: N/A  WA: 0,25 1368x743  1
    #1  * DG: 1368x768  VP: 0,0  WA: 0,25 1368x743  2
    #If your line does not have a "DG:" (desktop geometry) preceding the current resolution, you
    #  will need to change the regex below.
    if [[ ${line} =~ ([[:digit:]]+)[[:space:]]+\*[[:space:]]+DG:[[:space:]]+([[:alnum:]]+) ]]; then
      current_ws="${BASH_REMATCH[1]}"
      current_res="${BASH_REMATCH[2]}"
      target_res="${RESOLUTIONS[current_ws]}"
      #echo "Current workspace: ${current_ws}; Current resolution: ${current_res}; Target resolution: ${target_res}"
      if [[ ! ${current_res} =~ ${target_res} ]]; then
        #echo "Switching resolution to ${target_res}."
        xrandr -s ${target_res}
      fi
    fi
  done
}

while true
do
  check_and_change_res < <(wmctrl -d)
  #This waits for 1 second between checks. To change to a 5-second delay (e.g. performance), use:
  #sleep 5
  sleep 1
done