订阅屏幕关闭DBUS事件

订阅屏幕关闭DBUS事件

我的 MacBook Pro 具有键盘背光,这非常棒,但有一个小问题:屏幕会在一定时间后关闭,但键盘背光保持亮着。

我复制并弄乱了一个小型 DBUS Python 脚本来监视屏幕保护程序状态的变化,但它不会在屏幕关闭时触发,只会在屏幕保护程序激活或停用时触发:

from dbus.mainloop.glib import DBusGMainLoop

import dbus
import gobject
import logging

logging.basicConfig()

logger = logging.getLogger(__name__)

dbus_loop = DBusGMainLoop(set_as_default=True)

def message_callback(bus, message):
    if message.get_interface() == "org.gnome.ScreenSaver":
        if message.get_member() == "ActiveChanged":
            screensaver_enabled = bool(message.get_args_list()[0])
            logger.info("Screen saver changed. Active: %s", screensaver_enabled)

session = dbus.SessionBus(mainloop=dbus_loop)
session.add_match_string_non_blocking("interface='org.gnome.ScreenSaver'")

session.add_message_filter(message_callback)

logger.info("Starting up.")

loop = gobject.MainLoop()
loop.run()

只要屏幕保护程序被激活,此功能就很有效,但如果屏幕电源状态发生变化,此功能也不会改变,而屏幕电源状态的改变可能与屏幕保护程序无关。通过进入“设置”中的“亮度和锁定”,您可以将屏幕设置为 1 分钟后关闭,而不是锁定。然后,您可以将屏幕保护程序时间设置为不同的时间,例如 10 分钟。

我尝试在org.gnome.SettingsDaemon.Power.Screen接口上监听Changed信号,但这仅在手动改变屏幕亮度时才会发生。

我需要监听什么才能确定屏幕电源状态何时发生变化?我想编写一个脚本,在屏幕电源关闭时运行,这样我就可以禁用键盘背光。

答案1

好吧,由于我没有“声誉”,我无法发表评论,这太糟糕了。这更像是一条评论,而不是解决方案。

我一直在寻找类似的东西,而我正在监控“org.gnome.SessionManager.Presence”。我的显示器后面有用于偏置照明的 LED,我想用显示器关闭/打开它们。

如果我手动锁定计算机,此方法可行,但是,如果我以不同的时间间隔保留“屏幕关闭”和“锁定屏幕”设置,则当显示器关闭时,LED 也会关闭,但是,当屏幕保护程序锁定启动时,LED 会再次打开。

_getState () {
  dbus-monitor --session "type=signal,interface=org.gnome.SessionManager.Presence,member=StatusChanged" |
  while read x; do
    case "$x" in 
      *"uint32 3"*)
          /home/victor/bin/devices/kasa_cntrl.sh off
          echo -e "$(date)\t-\tTurned off" >> "$log"
          ;;
      *"uint32 0"*)
          /home/victor/bin/devices/kasa_cntrl.sh on
          echo -e "$(date)\t-\tTurned on" >> "$log"
          ;;
    esac
  done
}

参考https://www.organicdesign.co.nz/PoisonTap_solution

答案2

我刚刚安装了 Ubuntu 18.04,结果发现默认情况下没有屏幕保护程序。老实说,我不需要屏幕保护程序,所以我不会费心安装它。

但是,我发现 gnome 中的一些方法调用似乎可以解决问题:AddUserActiveWatch以及RemoveWatch来自org.gnome.Mutter.IdleMonitor界面的调用。

这是我的脚本:

#!/usr/bin/env python

import dbus, gobject
from dbus.mainloop.glib import DBusGMainLoop
from subprocess import call

def filter_cb(bus,message):
    if message.get_member() == "AddUserActiveWatch":
        print("Monitor off")
        call("/usr/bin/g810-led -dv 046d -dp c337 -a 000000", shell=True)
    elif message.get_member() == "RemoveWatch":
        print("Monitor on")
        call("/usr/bin/g810-led -dv 046d -dp c337 -p /etc/g810-led/profile", shell=True)
    return

DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()

bus.add_match_string_non_blocking("interface='org.gnome.Mutter.IdleMonitor',eavesdrop='true'")
bus.add_message_filter(filter_cb)

mainloop = gobject.MainLoop ()
mainloop.run ()

结果是:

  • 当显示屏开始变暗时,我的键盘背光就会关闭
  • 仅在我成功登录后键盘才会亮起,而不是在登录屏幕上亮起(但已经足够接近了)

免责声明:

  1. org.gnome.Mutter.IdleMonitor 中的单词“monitor”来自监控操作,而不是监视器(又称屏幕)。因此,基本上这些似乎是在用户被声明为空闲且 gnome 未空闲时调用的方法。事实上,在我的情况下,它与屏幕关闭同时发生。在您的情况下,可能并非如此。
  2. 显然你不能将其添加为 systemd 条目,因为它需要一个屏幕。但是,你可以在Startup ApplicationsGUI 中添加它,而且它可以工作
  3. 我正在使用 g810-led 可执行文件来打开和关闭键盘背光,这应该只是一个例子,因为它显然不适用于其他键盘

PS:发现一个“bug”。如果你中断屏幕淡入淡出,键盘将保持不亮状态。

答案3

好的,之后对此感到沮丧,我终于采取了一些措施并编写了一个 Python 实用程序脚本来监视 DBus 并正确接收会话锁定/解锁事件。

代码托管在这里,但我也会在下面包含它。我的目标是用 Rust 重写它,原因有很多,但主要是为了让人们更容易使用,而不必安装软件包来安装正确的 Python 库。


先决条件

要运行此代码,您需要:

  • 最近用的是 Python 3,我在 Python 3.8.5 上写了这个。
  • 蛋:
    • dbus-python >=1.2,<2
    • PyGObject >=3.36,<4

这些 Python egg 由某些 Ubuntu 软件包提供,但可能不是正确的版本。在 16.04 上,我相信所需的软件包是:

  • python3-gi,即PyGObject
  • python3-dbus,即dbus-python

在不同的发行版本上,这些包可能会有所不同。这是我想用 Rust 重写这篇文章的众多原因之一,我将在本答案的最后列出我的其他动机。

代码

让我们进入代码。

dbus-session-lock-watcher.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from dbus.mainloop.glib import DBusGMainLoop

from gi.repository import GLib

import dbus
import logging
import sys


class ScreenSaverEventListener(object):

    def __init__(self):
        self.logger = logging.getLogger(self.__class__.__name__)
        self.mainloop = DBusGMainLoop()
        self.loop = GLib.MainLoop()
        self.session_bus = dbus.SessionBus(mainloop=self.mainloop)

        self.receiver_args, self.receiver_kwargs = None, None

    def setup(self):
        self.receiver_args = (self.on_session_activity_change,)
        self.receiver_kwargs = dict(dbus_interface="org.freedesktop.DBus.Properties", path="/org/gnome/SessionManager",
                                    signal_name="PropertiesChanged",
                                    # callback arguments
                                    sender_keyword="sender", destination_keyword="dest",
                                    interface_keyword="interface", member_keyword="member", path_keyword="path",
                                    message_keyword="message")

        self.session_bus.add_signal_receiver(*self.receiver_args, **self.receiver_kwargs)

    def on_session_activity_change(self, target: dbus.String, changed_properties: dbus.Dictionary, *args, **kwargs):
        if target != "org.gnome.SessionManager" or "SessionIsActive" not in changed_properties:
            return

        if changed_properties.get("SessionIsActive"):
            self.on_session_unlock()
        else:
            self.on_session_lock()

    def on_session_lock(self):
        self.logger.info("Session Locked")

    def on_session_unlock(self):
        self.logger.info("Session Unlocked")

    def run(self):
        self.logger.debug("Starting event loop.")
        self.loop.run()

    def shutdown(self):
        self.logger.debug("Stopping event loop.")
        self.session_bus.remove_signal_receiver(*self.receiver_args, **self.receiver_kwargs)
        self.loop.quit()


def main():
    setup_logging()

    listener = ScreenSaverEventListener()
    listener.setup()

    try:
        listener.run()
    except KeyboardInterrupt:
        sys.stderr.write("ctrl+c received, shutting down...\n")
        listener.shutdown()


def setup_logging():
    console = logging.StreamHandler(sys.stderr)
    console.setFormatter(
        logging.Formatter("%(asctime)s [%(levelname)-5s] %(name)s: %(message)s", datefmt="%Y-%m-%dT%H:%M:%S%z"))
    logging.addLevelName(logging.WARNING, "WARN")
    logging.getLogger().addHandler(console)
    logging.getLogger().setLevel(logging.DEBUG)


if __name__ == "__main__":
    main()

为了使此代码执行任何有趣的操作,请编辑:

  • ScreenSaverEventListener.on_session_lock,它将在屏幕锁定时执行

相关内容