Мой 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
, но это происходит только тогда, когда яркость экрана изменяется вручную.
Что я могу прослушать, чтобы определить, когда состояние питания экрана изменилось? Я хочу написать сценарий, который запускается всякий раз, когда отключается питание экрана, чтобы я мог отключить подсветку клавиатуры.
Ну, это сосет это, я не могу оставить комментарий, потому что у меня нет "репутации". Это - больше комментария, чем решение.
я искал что-то подобное, и я контролирую 'org.gnome. SessionManager. Присутствие' вместо этого. У меня есть светодиоды позади моего монитора для освещения предвзятости, и я хочу повернуть их прочь/на с монитором.
Это работает, если я блокирую свой компьютер вручную, однако если я оставляю "экран" и "экран блокировки после того, как" настройки в различных интервалах, светодиоды выключают, когда монитор выключает, однако когда удары блокировки экранной заставки в нем включают светодиоды снова.
_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
}
<час> Я только что установил Ubuntu 18.04, и это поворачивается, там не подарок экранной заставки по умолчанию. И честно, я не хочу один, таким образом, я не потружусь устанавливать тот.
Однако я нашел некоторые вызовы метода от гнома, которые, кажется, добиваются цели: 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 ()
Результат:
Отказ от ответственности:
Startup Applications
GUI и это работаютPS: найденный "ошибкой". Если Вы прерываете экран, исчезают, клавиатура остается неосвещенной.
Хорошо, после лет разочарования по этому поводу, я, наконец, что-то сделал и написал служебный скрипт Python, который отслеживает DBus и правильно получает события блокировки/разблокировки сеанса.
Код размещен здесь, но я также приведу его ниже. Моя цель — переписать это на Rust по ряду причин, но в основном для того, чтобы облегчить людям использование без необходимости устанавливать пакеты для установки правильных библиотек Python.
Для запуска этого кода вам понадобятся:
dbus-python >=1.2,<2
PyGObject >=3.36,<4
Эти яйца Python предоставляются некоторыми пакетами 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
, который будет выполняться при блокировке экрана ScreenSaverEventListener.on_session_unlock
, который будет выполняться, когда экран будет разблокирован. Сначала сохраните приведенный выше скрипт в файл, желательно в ~/.local/bin
.Далее, конечно, нужно убедиться, что он помечен как исполняемый, через chmod +x ~/.local/bin/dbus-session-lock-watcher.py
.
Я бы посоветовал запускать это как службу, поэтому давайте создадим пользовательскую единицу systemd для запуска службы.
Вот пример устройства:
~/.config/systemd/user/session-lock-watcher.service
[Unit]
Description=DBus Session Lock Watcher
[Service]
ExecStart=$HOME/.local/bin/dbus-session-lock-watcher.py
[Install]
WantedBy=default.target
Далее включим службу:
systemctl --user enable session-lock-watcher.service
Наконец, запустим ее:
systemctl --user start session-lock-watcher.service
Для просмотреть журналы службы:
journalctl --user-unit session-lock-watcher.service
И теперь мы можем выполнять пользовательский код или сценарии всякий раз, когда сессия заблокирована или разблокирована!
На это ушло много работы, поэтому я хотел предоставить некоторую информацию на случай, если она будет интересной.
D-Bus сложен, но я попытаюсь обобщить его. D-Bus является связующим звеном между различными приложениями и предоставляет множество наворотов. Если вы знакомы с JMX для JVM, это похоже на то, за исключением того, что служебная шина является внешним процессом.
Как правило, существует две шины D-Bus: системная шина и сеансовая шина. системная шина предназначена для всей ОС, тогда как сеансовая шина предназначена для каждого пользователя и, скорее всего, является основной вещью, с которой вам нужно взаимодействовать.
D-Bus содержит реестр имен, каждое из которых имеет ряд путей к объектам, которые он поддерживает, и каждый из этих путей к объектам имеет один или несколько интерфейсов. Интерфейсы имеют методы, свойства и сигналы. Вы можете вызывать методы с аргументами или без них и получать результаты, если метод возвращает значения.Вы можете читать, а иногда и записывать свойства, а сигналы — это события, на которые вы можете подписаться и которые несут данные.
Точная связь между именами, путями к объектам и интерфейсами мне все еще не ясна, поэтому я просто собираюсь двигаться дальше. Короче говоря, у вас есть куча зарегистрированных имен/приложений, которые реализуют определенные интерфейсы, которые определяют методы, сигналы и свойства, и именно так вы взаимодействуете между приложениями, используя D-Bus.
Я попытаюсь найти дополнительную документацию, чтобы понять логику всего механизма D-Bus, но пока этого достаточно.
Что было исключительно разочаровывающим, так это то, сколько разных вещей я пытался просто получить обратный вызов, когда экран был заблокирован или разблокирован.
Я попробовал org.gnome.ScreenSaver
, который поддерживает несколько интерфейсов с одинаковыми атрибутами, org.freedesktop.ScreenSaver
и org.gnome.ScreenSaver
. . Оба они поддерживают сигнал ActiveChanged
, который несет логическое значение, по-видимому, для указания, активна ли заставка. Я никогда не ловил этот сигнал, никогда.
Я попробовал org.gnome.SessionManager
и его соответствующий интерфейс, который выдавал сигналы SessionOver
и SessionRunning
. Я никогда не ловил ни один из этих сигналов.
После перебора тонны различных имен, интерфейсов и сигналов я почти разочаровался в поиске сигнала, показывающего, когда сеанс был заблокирован и разблокирован.Я настроил общий приемник сигналов, который перехватывал все сигналы, устанавливал точку останова и проходил через все из них, пока не нашел сигнал изменения свойства для свойства SessionIsActive
, принадлежащего org. .gnome.SessionManager
. Бинго.
Наконец,Я настроил свой приемник сигналов на фильтрацию, насколько это возможно, при получении только соответствующих сигналов, когда это свойство изменено.
Я использую производную версию Ubuntu, поэтому этот код может работать не во всех дистрибутивах Ubuntu. Запустите сценарий, заблокируйте и разблокируйте сеанс и посмотрите, создает ли сценарий журналы блокировки и разблокировки.
Я бы очень хотел переписать все это в простом демоне Rust, так как бинарный файл будет гораздо проще распространять, с небольшим количеством зависимостей во время выполнения или вообще без них. Я также хотел бы предоставить какой-то каталог, в котором можно хранить столько скриптов, сколько они хотели бы запускать при событиях блокировки и разблокировки, возможно, что-то вроде ~/.config/nosleep/on-{lock,unlock }/*
. Будет получен отсортированный список файлов, и каждый исполняемый файл будет запускаться в отсортированном порядке, что значительно упростит написание сценариев для событий блокировки и разблокировки.
Надеюсь, это кому-нибудь поможет и будет информативным. Я знаю, что многому научился, когда писал это!
vte.sh
тогда, в случае, если Ваш Ubuntu является более старым. Оставьте те файлы под/etc/profile.d
нетронутыми, добавьте отрезанный, который я показал~/.bashrc
(с очевидной корректировкой имени файла, которое она отсылает к). – egmont 13 September 2016 в 03:55