切换显示器配置文件

作者: Kyle Rankin

这很有趣,当你的家庭办公室是你的沙发时,你往往会忘记当你停靠笔记本电脑并拥有显示器带来的所有额外的屏幕空间时,感觉有多好。多年来,我一直将我的工作笔记本电脑停靠在公司,当我在家工作时,我只是使用个人笔记本电脑通过 VPN 连接。但最近,我认识到划分个人生活和工作的好处,所以我开始随身携带笔记本电脑往返办公室。因为我们投资了扩展坞,所以在笔记本电脑放在腿上和笔记本电脑放在桌子上并配有额外的显示器之间进行切换相对简单——除了一件小事:我的外接显示器处于纵向模式。

大约在两年前,我开始喜欢纵向模式的宽屏显示器(图 1)。真的,我完成工作所需要的只是一个 Web 浏览器和几个终端,我发现如果我将 Web 浏览器放在笔记本电脑屏幕上,我可以在纵向模式显示器的所有垂直空间中容纳一个不错的大的屏幕会话或两个。这使得阅读 man 手册和其他文档变得很好,而且如果我需要比较两个终端的内容,我总是可以垂直分割我的屏幕(有关如何执行此操作的更多信息,请参阅我 2008 年 9 月刊的“Do the Splits”专栏: https://linuxjournal.cn/article/10159)。纵向模式的唯一问题是,所有 GUI 显示器配置工具往往都不能很好地处理纵向模式显示器,特别是当你想将它们与横向模式笔记本电脑屏幕结合使用时。因此,我发现我需要运行一个特殊的 xrandr 命令来设置显示器并确保它与我的笔记本电脑屏幕正确对齐。此外,每次我在停靠和未停靠模式之间切换时,我都需要将我的终端窗口从大型纵向模式显示器移动到笔记本电脑屏幕上的第二个桌面。这一切看起来都像是脚本可以为我解决的事情,因此在本文中,我将解释我用来从停靠模式过渡到未停靠模式的脚本。

图 1. Kyle 当前的桌面设置

基本上,我的脚本在运行时需要做两件事。首先,它需要运行适当的 xrandr 命令来启用或禁用外部显示器,其次,它需要将我的所有窗口重置到它们的默认位置。虽然我可以只在停靠时运行一个脚本,在未停靠时运行另一个脚本,但我可以从系统本身找出我的状态,所以我可以将所有内容都放在一个脚本中。我在我过去的两台工作笔记本电脑和 ThinkPad X220 上都设置了这样的脚本,在 ThinkPad X220 上,我能够使用一个 /sys 文件来衡量扩展坞的状态


#!/bin/bash
DOCKED=$(cat /sys/devices/platform/dock.0/docked)
case "$DOCKED" in
  "0")
     echo undocked
   ;;
   "1")
     echo docked
   ;;
esac

不幸的是,在我的新笔记本电脑(ThinkPad X230)上,这个文件不再能检测到扩展坞状态。起初我很恼火,但在撰写本专栏时,我意识到这使得该脚本对于每个没有扩展坞的人来说可能更有用。我的解决方法是使用 xrandr 本身来检查我的外接显示器所连接的视频设备的连接状态,该设备仅在我停靠时才存在。如果你在没有其他参数的情况下运行 xrandr,你将看到系统上许多不同潜在视频设备的列表


$ xrandr
Screen 0: minimum 320 x 200, current 1366 x 768, maximum 8192 x 8192
LVDS1 connected 1366x768+0+0 (normal left inverted right x axis y axis) 
 ↪277mm x 156mm
   1366x768       60.0*+
   1360x768       59.8     60.0  
   1024x768       60.0  
   800x600        60.3     56.2  
   640x480        59.9  
VGA1 disconnected (normal left inverted right x axis y axis)
HDMI1 disconnected (normal left inverted right x axis y axis)
DP1 disconnected (normal left inverted right x axis y axis)
HDMI2 disconnected (normal left inverted right x axis y axis)
HDMI3 disconnected (normal left inverted right x axis y axis)
DP2 disconnected (normal left inverted right x axis y axis)
DP3 disconnected (normal left inverted right x axis y axis)

在上面的例子中,笔记本电脑未停靠,因此只有主显示器 (LVDS1) 已连接。当我停靠设备并运行相同的命令时,我注意到我的显示器已连接到 HDMI3,因此我可以 grep HDMI3 的连接状态来检测我何时停靠。我的新骨架脚本看起来更像这样


#!/bin/bash
xrandr | grep -q "HDMI3 disconnected"
case "$?" in
  "0")
    echo undocked
  ;;
  "1")
    echo docked
  ;;
esac

在你的情况下,你将比较停靠时(或连接外部显示器时)和未停靠时 xrandr 的输出,并使用它来确定它对应于哪个设备。

既然我可以检测到我是否停靠,我应该做些什么。我需要做的第一件事是在我的外接显示器 (HDMI3) 上启用输出,告诉 xrandr 它位于我的笔记本电脑屏幕的右侧,并通过告诉 xrandr 将其向左旋转来将其设置为纵向模式


/usr/bin/xrandr --output HDMI3 --auto --right-of LVDS1 --rotate left

这工作正常;然而,纵向模式显示器和我的笔记本电脑在桌面上对齐的方式使得在两者之间移动鼠标非常尴尬。当我从笔记本电脑屏幕的顶部移动到最右边缘时,鼠标指针会向上移动一英尺到外接显示器的顶部。理想情况下,我希望鼠标指针在屏幕之间交叉时或多或少地对齐,但由于一个显示器是横向的,另一个是纵向的,我需要告诉 xrandr 将我的笔记本电脑显示器放置在虚拟桌面的较低位置。根据你各自的分辨率,这个位置需要一些调整,但我发现以下命令很好地对齐了我的两个显示器


/usr/bin/xrandr --output LVDS1 --pos 0x1152

这处理了我停靠时的屏幕,所以当我未停靠时,我基本上必须撤消我所做的任何上述更改。这意味着关闭 HDMI3 输出并将 LVDS1 的位置移回 0x0 坐标


/usr/bin/xrandr --output HDMI3 --off
/usr/bin/xrandr --output LVDS1 --pos 0x0

完整的 case 语句结果是


#!/bin/bash
xrandr | grep -q "HDMI3 disconnected"
case "$?" in
  "0") # undocked
    /usr/bin/xrandr --output HDMI3 --off
    /usr/bin/xrandr --output LVDS1 --pos 0x0
  ;;
  "1") # docked
    /usr/bin/xrandr --output HDMI3 --auto --right-of LVDS1 
    ↪--rotate left
    /usr/bin/xrandr --output LVDS1 --pos 0x1152
  ;;
esac

在我保存脚本后,我在我的桌面上绑定了一个组合键,我可以按下它来在每次我停靠或未停靠时执行它。当然,理想情况下,我会设置某种 udev 脚本或类似的东西来自动运行该脚本,但到目前为止,我还没有找到适合我的笔记本电脑的正确 hook。我做的唯一其他添加是在上面的 case 语句之后,我休眠一秒钟,然后调用一个 reset_windows shell 脚本,该脚本使用 wmctrl,就像我在 2008 年 11 月的 Hack 和 / 专栏“Memories of the Way Windows Were”(https://linuxjournal.cn/article/10213)中讨论的那样,只是它也包含相同的 case 语句,因此它在停靠时向一个方向移动窗口,在未停靠时向另一个方向移动窗口


#!/bin/bash
xrandr | grep -q "HDMI3 disconnected"
case "$?" in
        "0") # undocked
        wmctrl -r 'kyle-ThinkPad-X230' -t 1
        wmctrl -r 'kyle-ThinkPad-X230' -e '0,2,24,1362,362'
        wmctrl -r snowball -t 1
        wmctrl -r snowball -e '0,2,410,1362,328'
        ;;
        "1") # docked
        wmctrl -r 'kyle-ThinkPad-X230' -t 0
        wmctrl -r 'kyle-ThinkPad-X230' -e '0,1368,0,1080,1365'
        wmctrl -r snowball -t 0
        wmctrl -r snowball -e '0,1368,1387,1080,512'
        ;;
esac

当然,上面的 wmctrl 命令完全自定义为我的终端标题,但它应该可以作为开始你自己的命令的良好指南。在我的例子中,我希望在笔记本电脑模式下将两个终端移动到第二个桌面,并在停靠时移动到第一个桌面上的外接显示器。为什么不将两个脚本合并呢?嗯,我希望能够在停靠或未停靠之外有时重置我的窗口(这个脚本也绑定到不同的组合键)。最后,我有一组简单、易于修改的脚本,我可以用来保持窗口和我的桌面完全按照我想要的方式排列。

Kyle Rankin 是 Linux Journal 的技术编辑和专栏作家,也是 Purism 的首席安全官。他是 Linux Hardening in Hostile Networks, DevOps Troubleshooting, The Official Ubuntu Server Book, Knoppix Hacks, Knoppix Pocket Reference, Linux Multimedia HacksUbuntu Hacks 的作者,也是许多其他 O'Reilly 书籍的贡献者。Rankin 经常在安全和开源软件方面发表演讲,包括在 BsidesLV、O'Reilly Security Conference、OSCON、SCALE、CactusCon、Linux World Expo 和 Penguicon 上。你可以在 @kylerankin 上关注他。

加载 Disqus 评论