查询当前关注哪个监视器的简单方法

查询当前关注哪个监视器的简单方法

我想知道是否有一个工具可以查询当前关注的监视器。

我想要一个脚本来更改我当前使用的显示器上的壁纸(使用 xwallpaper)。据我了解,显示器编号或显示器名称(即 HDMI1、eDP1)都有助于实现我的目标。

最终,将使用具有以下结构的名称来复制为当前显示器选择的壁纸:wall-HDMI1.pngwall-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 脚本使用xrandrxdotool来完成此操作。

#!/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可以使用。根据您的调用方式,它可以返回标量值、数组或哈希值,这些值可以分配给变量或由其他函数使用。

顺便说一句,foreachandfor也是 perl 中的同义词。我写过的任何地方foreach,您都可以将其替换为for. for/foreach可用于迭代数组(列表),正如我在上面使用的那样,它也可以像 C 样式for ($i = 1; $i < 10; $i++)循环一样使用。

xdotool可能有 Perl 模块用于直接查询 X 以获取有关显示器和鼠标指针所需的信息,但与仅使用现有工具相比,我需要更长的时间才能找到它们并阅读它们的文档xrandr

相关内容