自动调暗非活动屏幕

自动调暗非活动屏幕

当有两个或多个显示器一起工作时,是否有任何方法可以通过发出如下命令将其中一个显示器置于待机/暂停状态:xset dpms force suspend?或为此目的设置一个时间,例如:xset dpms 100 0 0分别在这些显示器上工作?

我有两个互相工作的显示器,eDP1(我的笔记本电脑)和 VGA1(外部显示器)。

如果我没有直接与它们交互,我希望它们每个都分别进入挂起/待机模式,假设我在 VGA1 上看电影,并且一个半小时内 eDP1 什么也没做。

我对使用它不感兴趣,xrandor --off --output eDP1因为它的速度不够快。

我希望我的显示器能够正常工作,只需简单移动鼠标,我就能快速地在它们之间切换。

  • 运行:Ubuntu 18.04
  • 窗口管理器:OpenBox

答案1

无法使用 xset(实际上也是 X11)控制单个监视器

正如标题所示,这是不可能的,原因如下:如何 xset构建并由于其使用的 X11 函数。如果我们查看源代码,xset会发现调用DPMSForceLevel(dpy,DPMSModeSuspend)557 行),而显示变量dpy来自XOpenDisplay()函数(203 行),即定义

服务器与其屏幕和输入设备一起称为显示器。

换句话说,xset将设置全局应用于整个显示屏,而不是单个屏幕。要实现这一点,必须更改xset源代码。DPMS 扩展本身似乎大多只调用整个显示屏,而不是单个屏幕,因此甚至无法使用 X11 库编写自定义代码。

通过子系统手动控制该设置/sys似乎也不起作用

$ sudo bash -c 'echo Off > /sys/class/drm/card0-VGA-1/dpms'
[sudo] password for admin: 
bash: /sys/class/drm/card0-VGA-1/dpms: Permission denied

当发生键或鼠标事件时,屏幕也会退出 DPMS 模式,因此考虑到您可能想要移动鼠标或使用键盘,这两种操作都会导致显示器退出 DPMS 模式。

其他解决方法

最好的替代方案(也是实际可行的解决方案)是xrandr- 它可以用来控制单个“输出”。特别是,

xrandr --output VGA-1 -off

会关闭该输出。是的,您提到过您不想使用此解决方案,因为它不够快,但到目前为止,它是最好的解决方案。它有几个优点:

  • 不受键盘和鼠标事件的影响
  • 独立控制输出不同xset

xrandr --output VGA-1 --brightness 0.1虽然这是一个软件解决方案,但屏幕会以某种方式变色,使其看起来不亮,因此--brightness显示屏实际上并没有在硬件层面变暗,也没有在硬件层面关闭。但是,它可以使屏幕变暗,并且还可以抵抗按键/鼠标事件。

我查看了 Mate 和 Budgie 屏幕保护程序的源代码,它们都是 GNOME 屏幕保护程序的分支,但无论如何,它们似乎是一种软件解决方案,因为源代码中没有提及 DPMS。

答案2

临时评论

  1. 应楼主的要求,我制作了下面的脚本关闭屏幕通过xrandr。在更长时间的测试中,结果非常糟糕。并不是关闭失败,而是在重新激活屏幕时,屏幕布局完全混乱了。我很乐意发布它以查看它是否适用于您的情况,但我的建议是不要使用它。
    在脚本中,我回到将亮度设置为零。
  2. 关于我们是否应该定义主动监控器,有一些讨论通过鼠标位置, 或者按活动窗口的位置。如果不存在窗口,后者将不起作用。我们可能根本没有窗口(除了桌面本身),在这种情况下,选择变黑的窗口将是随机的(或者如果我们不包括例外,则会中断)。在我看来,唯一有意义的选择 - 并且在所有情况下都能以可预测的方式工作 - 是通过鼠标位置定义活动屏幕。此外,这也是窗口管理器决定新窗口应出现在哪里的方式。

那么我在这个版本中做了哪些改变呢?
默认情况下,空闲时间现在由键盘和鼠标活动定义。唤醒也由其中之一完成。


自动调暗非活动屏幕

正如我的同事们所说,从 CLI 单独关闭屏幕充其量是一个挑战,而且我也没有找到选择。

我确实找到了一种方法来自动暗淡所有屏幕,除了x 时间之后,鼠标所在的位置。

开始了

#!/usr/bin/env python3
import subprocess
import gi
gi.require_version("Gdk", "3.0")
from gi.repository import Gdk
import time
import sys


def get_idle():
    try:
        return int(subprocess.check_output("xprintidle")) / 1000
    except subprocess.CalledProcessError:
        return 0


def get_monitors():
    screen = Gdk.Screen.get_default()
    n_mons = display.get_n_monitors()
    mons = [screen.get_monitor_plug_name(i) for i in range(n_mons)]
    return mons


def set_mon_dimmed(mon, dim):
    print(mon, dim)
    val = "0.0" if dim else "1"
    try:
        subprocess.Popen(["xrandr", "--output", mon, "--brightness", val])
    except subprocess.CalledProcessError:
        print("oops")


def mousepos():
    # find out mouse location
    return Gdk.get_default_root_window().get_pointer()[1:3]


def get_currmonitor_atpos(x, y, display=None):
    """
    fetch the current monitor (obj) at position. display is optional to save
    fuel if it is already fetched elsewhere
    """
    if not display:
        display = Gdk.Display.get_default()
    return display.get_monitor_at_point(x, y)


display = Gdk.Display.get_default()
wait = int(sys.argv[1])
elapsed = 0
# set intervals to check
res = 2
monitors = [m for m in get_monitors()]
for m in monitors:
    set_mon_dimmed(m, False)

monrecord = {}
for m in monitors:
    monrecord[m] = {"idle": 0, "dimmed": False}

display = Gdk.Display.get_default()
idle1 = 0


while True:
    time.sleep(res)
    curr_mousepos = mousepos()
    activemon = get_currmonitor_atpos(
        curr_mousepos[0], curr_mousepos[1]
    ).get_model()
    idle2 = get_idle()
    if idle2 < idle1:
        monrecord[activemon]["idle"] = 0
        if monrecord[activemon]["dimmed"]:
            set_mon_dimmed(activemon, False)
            monrecord[activemon]["dimmed"] = False

    for m in monrecord.keys():
        curr_idle = monrecord[m]["idle"]
        print(m, curr_idle)
        if all([
            curr_idle > wait,
            monrecord[m]["dimmed"] is not True,
            m != activemon
        ]):
            set_mon_dimmed(m, True)
            monrecord[m]["dimmed"] = True         
        else:
            if m != activemon:
                monrecord[m]["idle"] = curr_idle + res

    idle1 = idle2

如何设置

设置很简单:

  1. 确保已python3-gi安装xprintidle

    sudo apt install python3-gi xprintidle
    
  2. 将上述脚本复制到一个空文件中,另存为dim_inactive,然后使其可执行

  3. 通过命令运行:

    /path/to/dim_inactive <idle_time_in_seconds>
    

    一个例子:

    /path/to/dim_inactive 120
    

    会使所有屏幕变暗鼠标不在的地方两分钟后

附加信息/说明

  • 该脚本在启动时列出所有屏幕
  • 记录空闲时间每台监视器(可能超过 2)。如果监视器在 x 秒内未被访问,则监视器将变黑,除了鼠标所在的显示器
  • 根据一个好的(但坏的)传统,Gnome 不断破坏一些东西并不断更改 API。因此,在 19.04 及更高版本上运行此脚本时,您会收到一些已弃用的警告。同时,它不会在 PEP8 上中断。无论如何都会更新到最新的 API。

答案3

多年来,我一直将笔记本电脑设置为当盖子关闭时,笔记本电脑将处于挂起状态,并且外接显示器将变为空白。

由于您想在外接显示器上观看 90 分钟的视频,并且笔记本电脑屏幕变黑,我将盖子关闭选项更改为“不执行任何操作”:

  • 优点:当我关闭笔记本电脑盖时,所有笔记本电脑窗口都会进入全屏视频。
  • 优点:当我打开笔记本电脑盖时,窗口会恢复,并且不再位于全屏视频下方。
  • 缺点:我必须使视频非全屏才能访问顶部栏菜单,从齿轮菜单中选择暂停。
  • 优点:当系统通过外接显示器上的菜单暂停时,打开笔记本电脑盖仍可恢复系统。

我没有使用 DPMS 作为外接显示器,但您可以使用xset q命令检查您的设置:

$ xset q
Keyboard Control:
  auto repeat:  on    key click percent:  0    LED mask:  00000002
  XKB indicators:
    00: Caps Lock:   off    01: Num Lock:    on     02: Scroll Lock: off
    03: Compose:     off    04: Kana:        off    05: Sleep:       off
    06: Suspend:     off    07: Mute:        off    08: Misc:        off
    09: Mail:        off    10: Charging:    off    11: Shift Lock:  off
    12: Group 2:     off    13: Mouse Keys:  off
  auto repeat delay:  500    repeat rate:  33
  auto repeating keys:  00ffffffdffffbbf
                        fadfffefffedffff
                        9fffffffffffffff
                        fff7ffffffffffff
  bell percent:  50    bell pitch:  400    bell duration:  100
Pointer Control:
  acceleration:  5/1    threshold:  5
Screen Saver:
  prefer blanking:  yes    allow exposures:  yes
  timeout:  0    cycle:  0
Colors:
  default colormap:  0xb3    BlackPixel:  0x0    WhitePixel:  0xffffff
Font Path:
  /usr/share/fonts/X11/misc,/usr/share/fonts/X11/Type1,built-ins
DPMS (Energy Star):
  Standby: 0    Suspend: 0    Off: 0
  DPMS is Disabled

请注意以下几行:

Screen Saver:
  prefer blanking:  yes
  • 你可能想要prefer blanking: no

还请注意以下几行:

DPMS (Energy Star):
  Standby: 0    Suspend: 0    Off: 0
  DPMS is Disabled
  • 您可能希望在需要时DPMS is enabled设置监视器Standby

希望其他用户已经使用过这些选项并为您发布详细的答案。

答案4

如果显示器有 ddc 可以工作 - 它也相对较慢,但几乎是减少没有 xrandr 的 ccfl 背光显示器功耗的唯一方法。不幸的是,只有一半的显示器实现了它。

$ ddccontrol dev:/dev/i2c-4
...
        > Power control
                > id=dpms, name=DPMS Control, address=0xd6, delay=-1ms, type=2
                  Possible values:
                        > id=on - name=On, value=1
                        > id=standby - name=Standby, value=4
...

您可以通过查看 Sergiy 上面提到的 /sys/class/drm/card* 目录(即它们的 i2c* 子目录)轻松地将 i2c 总线连接到输出。

因此,“ddccontrol dev:/dev/i2c-4 -r 0xd6 -w 4”将其设置为待机,“ddccontrol dev:/dev/i2c-4 -r 0xd6 -w 1”将其再次设置为开启。

我设置了 cronjobs,包括 getIdlehttps://github.com/IonicaBizau/screensaver/blob/master/getIdle.c给出 X 空闲时间(以毫秒为单位)。

您可能应该将您的用户添加到 i2c 组或以其他方式启用对 ddc 总线的访问。

另一个怪癖:虽然 ddc 控制标记为“电源”,但至少在我的显示器上我不能用它来确定实际的电源状态 - 如果我手动关闭显示器,它仍然显示“开”并且我无法通过 i2c 打开它。

相关内容