我想知道是否有一个工具可以查询当前关注的监视器。
我想要一个脚本来更改我当前使用的显示器上的壁纸(使用 xwallpaper)。据我了解,显示器编号或显示器名称(即 HDMI1、eDP1)都有助于实现我的目标。
最终,将使用具有以下结构的名称来复制为当前显示器选择的壁纸:wall-HDMI1.png
或wall-eDP1.png
有一个部分解决方案,其中接受的答案建议使用鼠标的位置猜测当前显示器使用xdotool
。这充其量看起来很挑剔。特别是在可以使用多种不同布局连接到多个显示器的笔记本电脑上。
答案1
如果您想知道当前的主监视器是什么,这很简单:
$ xrandr | grep primary
DP-2 connected primary 2560x1440+0+0 (normal left inverted right x axis y axis) 597mm x 336mm
或者
$ xrandr | awk '/ primary / {print $1}'
DP-2
如果您想知道鼠标指针当前位于哪个显示器上,那就有点困难了。首先,您必须找出鼠标指针的位置(例如,使用xdotool
),然后您必须将鼠标的 X、Y 坐标与每个屏幕的起始和结束 X 和 Y 坐标进行比较(例如,使用xrandr
)。
以下 perl 脚本使用xrandr
和xdotool
来完成此操作。
#!/usr/bin/perl
use strict;
my %M; # hash to hold keys from 'xdotool getmouselocation --shell'
my $pipe; # file handle for the pipes we're going to open
# get the current cursor location into $M{X} and $M{Y}
open($pipe, "-|", qw(xdotool getmouselocation --shell)) ||
die "couldn't open pipe from xdotool: $!\n";
while(<$pipe>) {
chomp;
my($key, $val) = split /=/;
$M{$key} = $val;
};
close($pipe);
# compare mouse location to monitor co-ordinates
open($pipe, "-|", "xrandr") ||
die "couldn't open pipe from xrandr: $!\n";
while(<$pipe>) {
my ($display, $width, $height, $x_offset, $y_offset);
next unless m/ connected /;
my @F = split;
$display = $F[0];
# co-ordinates are on fourth field (F[3]) on the primary display
# or on third field (F[2]) on non-primary displays. Perl arrays
# start from zero, not one (same as in bash).
if ($F[2] eq "primary") {
($width, $height, $x_offset, $y_offset) = split /[x+]/, $F[3];
} else {
($width, $height, $x_offset, $y_offset) = split /[x+]/, $F[2];
};
if ($M{X} >= $x_offset && $M{X} <= $width + $x_offset &&
$M{Y} >= $y_offset && $M{Y} <= $height + $y_offset) {
print "$display\n";
last;
};
};
close($pipe);
将其另存为,例如./get-focused-monitor.pl
,使用 使其可执行chmod +x get-focused-monitor.pl
,然后运行它。它会告诉您鼠标指针当前悬停在哪个监视器上。
$ ./get-focused-monitor.pl
DP-2
运行它,watch -n 0.1 ./get-focused-monitor.pl
当您将鼠标从一个显示器移动到另一个显示器时,您会兴奋地看到显示器名称发生变化。我这样做了几十秒钟,兴奋的心情从未改变。这几乎太多了。
顺便说一句,我目前连接了两台 1440p 显示器。横向模式下的 27 英寸显示器(“DP-2”,主显示器)和转向纵向模式的 24 英寸显示器(“DP-0”)。该脚本在我的系统上解析的输出xrandr
如下所示:
$ xrandr | grep \\bconnected
DP-0 connected 1440x2560+2560+0 left (normal left inverted right x axis y axis) 527mm x 296mm
DP-2 connected primary 2560x1440+0+0 (normal left inverted right x axis y axis) 597mm x 336mm
split /[x+]/
脚本中使用x
或+
作为分隔符将第三个或第四个字段拆分为四个整数的数组:宽度、高度、x 偏移量和 y 偏移量。请参阅perldoc -f split
参考资料 了解有关 perlsplit()
函数如何工作的信息。
PS:为什么是perl?因为 Perl 更容易 - 在 shell 中的 while-read 循环中解析文本很糟糕,并且在 shell 中进行计算至少同样糟糕,所以我不想处理通常的 shell 空白、引用和分词问题,或者数组的 shell 语法异常丑陋。 shell 并不是唯一可以通过管道将数据传入或传出外部程序的语言。 UNIX 和 Linux 上常用的大多数语言都可以做到这一点。
在 shell 中做这件事并不是特别困难,只是笨拙和别扭。如果您愿意,可以随意在 bash 中重新实现算法,重要的是方法,而不是语言。
顺便说一句,在 shell 中执行此操作的最简单方法是运行类似命令(xdotool getmouselocation --shell; xrandr) | awk '...'
并让 awk 解析来自两个程序的不同类型的输入。或者使用 perl 代替 awk。另外值得注意的是,像 shell 和 perl 一样,awk 也可以运行外部程序并处理它们的输出。
Perl 中还有更多的设置工作(显式打开管道),但那是样板内容。其余的代码比在 shell 中容易得多。
我可以使用反引号或qx()
(参见perldoc -f qx
)来做命令替换就像在 shell 中一样,而不是使用open()
(请参阅 参考资料perldoc -f open
)。例如:
#!/usr/bin/perl
foreach (qx(xdotool getmouselocation --shell)) {
m/^([^=]+)=(.*)$/;
$M{$1} = $2;
};
foreach (qx(xrandr)) {
next unless m/connected/;
($d, $w, $h, $xo, $yo) = m/^([^\s]+) .* (\d+)x(\d+)\+(\d+)\+(\d+)/;
if ($M{X} >= $xo && $M{X} <= $w + $xo &&
$M{Y} >= $yo && $M{Y} <= $h + $yo) {
print "$d\n";
last;
}
}
这会产生与第一个脚本完全相同的输出,但使用 perl 样式命令替换而不是显式open()
-ing 管道。它还使用正则表达式捕获组来提取数据,而不是split()
.
顺便说一句,perl 有一个非常有用的函数,称为map
.请参阅perfoc -f map
参考资料 详细信息(另请参阅perldoc -f grep
类似有用的内置程序)。它是一个非常有用且多功能的工具 - 您可以使用它做的最简单的事情之一就是执行以下操作,例如将第一个foreach
循环替换为:
foreach (qx(xdotool getmouselocation --shell)) {
map { $M{$1} = $2 } m/^([^=]+)=(.*)$/;
};
甚至:
map { $M{$1} = $2 } m/^([^=]+)=(.*)$/ foreach (qx(xdotool getmouselocation --shell));
两个版本基本上是相同的:map { CODE BLOCK } ARRAY
- 数组可以是已经存在的变量,也可以是匿名数组,以任何适合您的方式即时生成。
在列表上下文(一个或多个匹配项)中调用时,正则表达式返回一个数组。在上面的示例中,正则表达式匹配运算符 ( m/PATTERN/
) 应用于foreach
循环的每个元素,循环依次迭代数组的元素(本例中的数组是 的输出xdotool getmouselocation --shell
,每个数组元素一行)。
还有其他方法map
可以使用。根据您的调用方式,它可以返回标量值、数组或哈希值,这些值可以分配给变量或由其他函数使用。
顺便说一句,foreach
andfor
也是 perl 中的同义词。我写过的任何地方foreach
,您都可以将其替换为for
. for
/foreach
可用于迭代数组(列表),正如我在上面使用的那样,它也可以像 C 样式for ($i = 1; $i < 10; $i++)
循环一样使用。
xdotool
可能有 Perl 模块用于直接查询 X 以获取有关显示器和鼠标指针所需的信息,但与仅使用现有工具相比,我需要更长的时间才能找到它们并阅读它们的文档xrandr
。