Refresh lid observations before rendering after resume
This commit is contained in:
1 parent
0420e59cbe
commit
428267a714
6 files changed
+406
-52
No files matched your search
@@ -6,6 +6,7 @@ use std::path::Path;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
mod capture;
|
||||
mod lid;
|
||||
mod output_power;
|
||||
mod planes;
|
||||
mod redraw;
|
||||
@@ -53,7 +54,8 @@ use crate::render::{frame_effect_metrics, sampled_output_elements};
|
||||
pub struct DirectBackendState {
|
||||
pub session: LibSeatSession,
|
||||
pub active: bool,
|
||||
lid_closed: bool,
|
||||
lid: lid::LidState,
|
||||
lid_reader: lid::Reader,
|
||||
topology: Topology,
|
||||
pub(crate) low_power: bool,
|
||||
devices: HashMap<DrmNode, DirectDevice>,
|
||||
@@ -69,7 +71,7 @@ pub struct DirectBackendState {
|
||||
|
||||
impl DirectBackendState {
|
||||
fn can_render(&self) -> bool {
|
||||
self.active && !self.topology.reconciling
|
||||
self.active && !self.topology.reconciling && !self.lid.pending()
|
||||
}
|
||||
|
||||
pub(crate) fn dump_scheduling_metrics(&self) {
|
||||
@@ -239,10 +241,12 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
|
||||
libinput_context.suspend();
|
||||
}
|
||||
|
||||
let lid_reader = lid::Reader::start(state)?;
|
||||
state.direct_backend = Some(DirectBackendState {
|
||||
session,
|
||||
active: session_active,
|
||||
lid_closed: initial_lid_closed(),
|
||||
lid: lid::LidState::default(),
|
||||
lid_reader,
|
||||
topology: Topology::default(),
|
||||
low_power: super::power::low_power(false, super::power::battery_percent()),
|
||||
devices: HashMap::new(),
|
||||
@@ -263,8 +267,7 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
|
||||
.topology
|
||||
.devices
|
||||
.insert(primary_path, Default::default());
|
||||
reconcile_outputs(state, false);
|
||||
sync_battery_timer(state);
|
||||
lid::request_refresh(state, false);
|
||||
|
||||
event_loop
|
||||
.handle()
|
||||
@@ -291,6 +294,10 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
|
||||
let handle = state.loop_handle.clone();
|
||||
if let Some(backend) = state.direct_backend.as_mut() {
|
||||
backend.active = false;
|
||||
backend.lid.pause();
|
||||
if let Some(token) = backend.lid_reader.deadline.take() {
|
||||
handle.remove(token);
|
||||
}
|
||||
backend.topology.dirty = true;
|
||||
if let Some(token) = backend.topology.retry_timer.take() {
|
||||
handle.remove(token);
|
||||
@@ -336,12 +343,8 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
|
||||
|
||||
let seat = state.seat.clone();
|
||||
state.idle_notifier_state.notify_activity(&seat);
|
||||
reconcile_outputs(state, true);
|
||||
sync_battery_timer(state);
|
||||
state.reset_animation_clock();
|
||||
tracing::info!("direct session activated");
|
||||
state.lock_input_activity();
|
||||
render_all(state);
|
||||
lid::request_refresh(state, true);
|
||||
tracing::info!("direct session reacquired; refreshing external state");
|
||||
}
|
||||
})?;
|
||||
event_loop.handle().insert_source(udev_backend, |event, _, state| {
|
||||
@@ -468,7 +471,7 @@ pub(crate) fn validate_live_outputs(state: &Ferese, profiles: &[OutputProfile])
|
||||
let mut usable = 0;
|
||||
for device in backend.devices.values() {
|
||||
let mut scan = select_outputs(&device.drm, profiles, backend.low_power).map_err(|e| e.to_string())?;
|
||||
apply_lid_policy(&mut scan, backend.lid_closed);
|
||||
apply_lid_policy(&mut scan, backend.lid.closed());
|
||||
usable += scan.selections.len();
|
||||
}
|
||||
if usable == 0 {
|
||||
@@ -864,22 +867,19 @@ pub(crate) fn set_lid_closed(state: &mut Ferese, closed: bool) {
|
||||
let Some(backend) = state.direct_backend.as_mut() else {
|
||||
return;
|
||||
};
|
||||
if backend.lid_closed == closed {
|
||||
return;
|
||||
}
|
||||
backend.lid_closed = closed;
|
||||
// Even an unchanged switch observation supersedes an in-flight DBus read.
|
||||
if backend.lid.observe(closed) {
|
||||
tracing::info!(closed, "laptop lid state changed");
|
||||
reconcile_outputs(state, false);
|
||||
}
|
||||
}
|
||||
|
||||
fn initial_lid_closed() -> bool {
|
||||
std::fs::read_dir("/proc/acpi/button/lid")
|
||||
.ok()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter_map(Result::ok)
|
||||
.filter_map(|entry| std::fs::read_to_string(entry.path().join("state")).ok())
|
||||
.any(|state| state.split_whitespace().last() == Some("closed"))
|
||||
pub(crate) fn system_resumed(state: &mut Ferese) {
|
||||
if state.direct_backend.as_ref().is_some_and(|backend| backend.active) {
|
||||
let seat = state.seat.clone();
|
||||
state.idle_notifier_state.notify_activity(&seat);
|
||||
}
|
||||
lid::request_refresh(state, true);
|
||||
}
|
||||
|
||||
fn internal_connector(interface: connector::Interface) -> bool {
|
||||
@@ -1122,7 +1122,10 @@ fn rescan_device(state: &mut Ferese, node: DrmNode) -> Result<bool, ()> {
|
||||
}
|
||||
};
|
||||
|
||||
let lid_closed = state.direct_backend.as_ref().is_some_and(|backend| backend.lid_closed);
|
||||
let lid_closed = state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.is_some_and(|backend| backend.lid.closed());
|
||||
apply_lid_policy(&mut scan, lid_closed);
|
||||
device.connected_outputs = scan.connected_outputs;
|
||||
let mut complete = true;
|
||||
@@ -1562,6 +1565,7 @@ fn deliver_frame_callbacks(state: &mut Ferese, node: DrmNode, crtc: crtc::Handle
|
||||
output.callback_timer = None;
|
||||
if !backend.active
|
||||
|| backend.topology.reconciling
|
||||
|| backend.lid.pending()
|
||||
|| !device.drm.is_active()
|
||||
|| !output.power.can_render()
|
||||
|| state.session_lock.sleeping
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
//! Resume reads are asynchronous; newer switch events win over old replies.
|
||||
use std::sync::mpsc;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(super) struct Probe {
|
||||
serial: u64,
|
||||
revision: u64,
|
||||
reactivate: bool,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct LidState {
|
||||
value: Option<bool>,
|
||||
revision: u64,
|
||||
serial: u64,
|
||||
pending: Option<Probe>,
|
||||
}
|
||||
|
||||
impl LidState {
|
||||
pub fn closed(&self) -> bool {
|
||||
self.value == Some(true)
|
||||
}
|
||||
pub fn pending(&self) -> bool {
|
||||
self.pending.is_some()
|
||||
}
|
||||
|
||||
pub fn observe(&mut self, closed: bool) -> bool {
|
||||
let changed = self.value != Some(closed);
|
||||
self.value = Some(closed);
|
||||
self.revision = self.revision.wrapping_add(1);
|
||||
changed
|
||||
}
|
||||
|
||||
fn begin(&mut self, reactivate: bool) -> Probe {
|
||||
self.serial = self.serial.wrapping_add(1);
|
||||
let probe = Probe {
|
||||
serial: self.serial,
|
||||
revision: self.revision,
|
||||
reactivate: reactivate || self.pending.is_some_and(|pending| pending.reactivate),
|
||||
};
|
||||
self.pending = Some(probe);
|
||||
probe
|
||||
}
|
||||
|
||||
fn complete(&mut self, probe: Probe, value: Option<bool>) -> Option<bool> {
|
||||
if self.pending != Some(probe) {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.pending = None;
|
||||
if probe.revision == self.revision
|
||||
&& let Some(value) = value
|
||||
{
|
||||
self.observe(value);
|
||||
}
|
||||
Some(probe.reactivate)
|
||||
}
|
||||
|
||||
pub fn pause(&mut self) {
|
||||
self.pending = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct Reader {
|
||||
requests: mpsc::Sender<Probe>,
|
||||
pub deadline: Option<RegistrationToken>,
|
||||
}
|
||||
|
||||
fn read_logind(connection: &zbus::blocking::Connection) -> zbus::Result<bool> {
|
||||
// A direct Get avoids retaining a property cache across suspend.
|
||||
let value: zbus::zvariant::OwnedValue = connection
|
||||
.call_method(
|
||||
Some("org.freedesktop.login1"),
|
||||
"/org/freedesktop/login1",
|
||||
Some("org.freedesktop.DBus.Properties"),
|
||||
"Get",
|
||||
&("org.freedesktop.login1.Manager", "LidClosed"),
|
||||
)?
|
||||
.body()
|
||||
.deserialize()?;
|
||||
bool::try_from(value).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn read_acpi() -> Option<bool> {
|
||||
let mut state = None;
|
||||
for entry in std::fs::read_dir("/proc/acpi/button/lid").ok()?.flatten() {
|
||||
let Ok(text) = std::fs::read_to_string(entry.path().join("state")) else {
|
||||
continue;
|
||||
};
|
||||
match text.split_whitespace().last() {
|
||||
Some("closed") => return Some(true),
|
||||
Some("open") => state = Some(false),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
state
|
||||
}
|
||||
|
||||
impl Reader {
|
||||
pub fn start(state: &Ferese) -> Result<Self, Box<dyn Error>> {
|
||||
let (requests, receiver) = mpsc::channel::<Probe>();
|
||||
let (replies, events) = smithay::reexports::calloop::channel::channel();
|
||||
state.loop_handle.insert_source(events, |event, _, state| {
|
||||
if let smithay::reexports::calloop::channel::Event::Msg((probe, value)) = event {
|
||||
complete_refresh(state, probe, value);
|
||||
}
|
||||
})?;
|
||||
|
||||
std::thread::Builder::new()
|
||||
.name("ferese-lid-reader".into())
|
||||
.spawn(move || {
|
||||
while let Ok(mut probe) = receiver.recv() {
|
||||
for newer in receiver.try_iter() {
|
||||
probe = newer;
|
||||
}
|
||||
let value = zbus::blocking::connection::Builder::system()
|
||||
.and_then(|builder| builder.method_timeout(Duration::from_secs(2)).build())
|
||||
.and_then(|connection| read_logind(&connection))
|
||||
.ok()
|
||||
.or_else(read_acpi);
|
||||
if replies.send((probe, value)).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(Self {
|
||||
requests,
|
||||
deadline: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn request_refresh(state: &mut Ferese, reactivate: bool) {
|
||||
let Some(backend) = state.direct_backend.as_mut() else {
|
||||
return;
|
||||
};
|
||||
if !backend.active {
|
||||
backend.topology.dirty = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(token) = backend.lid_reader.deadline.take() {
|
||||
state.loop_handle.remove(token);
|
||||
}
|
||||
// A timer that fires while readiness is gated would otherwise leave a
|
||||
// stale registration token on an unchanged output.
|
||||
for device in backend.devices.values_mut() {
|
||||
for output in device.outputs.values_mut() {
|
||||
cancel_output_timers(&state.loop_handle, output);
|
||||
}
|
||||
}
|
||||
let probe = backend.lid.begin(reactivate);
|
||||
let sent = backend.lid_reader.requests.send(probe).is_ok();
|
||||
let timer = state
|
||||
.loop_handle
|
||||
.insert_source(Timer::from_duration(Duration::from_secs(3)), move |_, _, state| {
|
||||
if let Some(backend) = state.direct_backend.as_mut() {
|
||||
backend.lid_reader.deadline = None;
|
||||
}
|
||||
tracing::warn!("lid refresh deadline elapsed; retaining the latest observed state");
|
||||
complete_refresh(state, probe, None);
|
||||
TimeoutAction::Drop
|
||||
});
|
||||
|
||||
if sent && let Ok(token) = timer {
|
||||
state.direct_backend.as_mut().unwrap().lid_reader.deadline = Some(token);
|
||||
} else {
|
||||
if let Ok(token) = timer {
|
||||
state.loop_handle.remove(token);
|
||||
}
|
||||
complete_refresh(state, probe, None);
|
||||
}
|
||||
}
|
||||
|
||||
fn complete_refresh(state: &mut Ferese, probe: Probe, value: Option<bool>) {
|
||||
let Some(backend) = state.direct_backend.as_mut() else {
|
||||
return;
|
||||
};
|
||||
let Some(reactivate) = backend.lid.complete(probe, value) else {
|
||||
return;
|
||||
};
|
||||
if let Some(token) = backend.lid_reader.deadline.take() {
|
||||
state.loop_handle.remove(token);
|
||||
}
|
||||
if !backend.active {
|
||||
return;
|
||||
}
|
||||
|
||||
// Waking a locked session may request redraws. Hold them until the fresh
|
||||
// device/connector snapshot has been reconciled.
|
||||
backend.topology.reconciling = true;
|
||||
state.reset_animation_clock();
|
||||
state.lock_input_activity();
|
||||
state.direct_backend.as_mut().unwrap().topology.reconciling = false;
|
||||
reconcile_outputs(state, reactivate);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[ignore = "requires dbus-daemon and private test sockets"]
|
||||
fn logind_reads_are_fresh_without_property_change_signals() {
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
struct Bus(std::process::Child);
|
||||
impl Drop for Bus {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.0.kill();
|
||||
let _ = self.0.wait();
|
||||
}
|
||||
}
|
||||
struct Manager(Arc<AtomicBool>);
|
||||
#[zbus::interface(name = "org.freedesktop.login1.Manager")]
|
||||
impl Manager {
|
||||
#[zbus(property)]
|
||||
fn lid_closed(&self) -> bool {
|
||||
self.0.load(Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
let mut bus = Bus(Command::new("dbus-daemon")
|
||||
.args(["--session", "--nofork", "--print-address=1"])
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap());
|
||||
let mut address = String::new();
|
||||
BufReader::new(bus.0.stdout.take().unwrap())
|
||||
.read_line(&mut address)
|
||||
.unwrap();
|
||||
let closed = Arc::new(AtomicBool::new(true));
|
||||
let server = zbus::blocking::connection::Builder::address(address.trim())
|
||||
.unwrap()
|
||||
.name("org.freedesktop.login1")
|
||||
.unwrap()
|
||||
.serve_at("/org/freedesktop/login1", Manager(closed.clone()))
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
let client = zbus::blocking::connection::Builder::address(address.trim())
|
||||
.unwrap()
|
||||
.method_timeout(Duration::from_secs(1))
|
||||
.build()
|
||||
.unwrap();
|
||||
assert!(read_logind(&client).unwrap());
|
||||
closed.store(false, Ordering::Relaxed);
|
||||
assert!(!read_logind(&client).unwrap());
|
||||
server.release_name("org.freedesktop.login1").unwrap();
|
||||
assert!(
|
||||
read_logind(&client).is_err(),
|
||||
"missing logind must not masquerade as an open lid"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn newer_input_wins_over_a_delayed_resume_read() {
|
||||
let mut lid = LidState::default();
|
||||
lid.observe(true);
|
||||
let probe = lid.begin(true);
|
||||
lid.observe(false);
|
||||
assert_eq!(lid.complete(probe, Some(true)), Some(true));
|
||||
assert!(!lid.closed());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_reads_preserve_known_state_and_unknown_is_not_an_open_observation() {
|
||||
let mut lid = LidState::default();
|
||||
let probe = lid.begin(false);
|
||||
lid.complete(probe, None);
|
||||
assert_eq!(lid.value, None);
|
||||
lid.observe(true);
|
||||
let probe = lid.begin(true);
|
||||
lid.complete(probe, None);
|
||||
assert_eq!(lid.value, Some(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pause_and_newer_resume_invalidate_old_replies() {
|
||||
let mut lid = LidState::default();
|
||||
let old = lid.begin(true);
|
||||
lid.pause();
|
||||
assert_eq!(lid.complete(old, Some(true)), None);
|
||||
let current = lid.begin(true);
|
||||
assert_eq!(lid.complete(old, Some(true)), None);
|
||||
assert!(lid.pending());
|
||||
assert_eq!(lid.complete(current, Some(false)), Some(true));
|
||||
assert!(!lid.pending());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn system_wake_cannot_cancel_pending_session_reactivation() {
|
||||
let mut lid = LidState::default();
|
||||
lid.begin(true);
|
||||
let probe = lid.begin(false);
|
||||
assert_eq!(lid.complete(probe, Some(false)), Some(true));
|
||||
}
|
||||
}
|
||||
@@ -114,12 +114,15 @@ pub(super) fn reconcile_outputs(state: &mut Ferese, reactivate: bool) {
|
||||
let Some(backend) = state.direct_backend.as_mut() else {
|
||||
return;
|
||||
};
|
||||
if !backend.topology.request(backend.active) {
|
||||
if !backend.topology.request(backend.active && !backend.lid.pending()) {
|
||||
return;
|
||||
}
|
||||
|
||||
backend.topology.reconciling = true;
|
||||
backend.topology.dirty = false;
|
||||
if reactivate {
|
||||
backend.presentation.values_mut().for_each(PresentationClock::reset_timing);
|
||||
}
|
||||
if let Some(token) = backend.topology.retry_timer.take() {
|
||||
state.loop_handle.remove(token);
|
||||
}
|
||||
@@ -198,6 +201,15 @@ pub(super) fn reconcile_outputs(state: &mut Ferese, reactivate: bool) {
|
||||
let device = state.direct_backend.as_mut().unwrap().devices.get_mut(&node).unwrap();
|
||||
device.drm.activate(true).map(|()| {
|
||||
for (crtc, output) in &mut device.outputs {
|
||||
cancel_output_timers(&state.loop_handle, output);
|
||||
output.surface.reset_buffers();
|
||||
output.primary_commit = None;
|
||||
output.capture_texture = None;
|
||||
output.frame_pending = false;
|
||||
output.lock_frame_pending = false;
|
||||
output.power.suspended();
|
||||
state.display_presentation.remove_output(&output.output);
|
||||
|
||||
if let Err(error) = output.surface.clear().and_then(|()| output.surface.reset_state()) {
|
||||
tracing::warn!(%error, ?node, ?crtc, "output reset failed; recreating output");
|
||||
failed_outputs.push(*crtc);
|
||||
@@ -291,6 +303,7 @@ pub(super) fn reconcile_outputs(state: &mut Ferese, reactivate: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
state.refresh_idle_inhibition();
|
||||
sync_battery_timer(state);
|
||||
state.restore_output_focus();
|
||||
state.relayout();
|
||||
|
||||
@@ -27,6 +27,7 @@ mod render;
|
||||
#[cfg(feature = "resize-metrics")]
|
||||
mod resize_metrics;
|
||||
mod resize_transaction;
|
||||
mod resume;
|
||||
mod session_lock;
|
||||
mod shell_control;
|
||||
mod stacking;
|
||||
@@ -93,6 +94,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
let mut state = Ferese::new(&mut event_loop, display, runtime)?;
|
||||
state.config_source = initial_source.filter(|source| source.len() <= 60 * 1024);
|
||||
theme::init(&mut event_loop, &mut state, candidate)?;
|
||||
resume::init(&mut event_loop)?;
|
||||
overview::init_font_loader(&mut event_loop, &mut state)?;
|
||||
backends::init(launch.backend, &mut event_loop, &mut state)?;
|
||||
info!(socket = ?state.socket_name, backend = ?launch.backend, "Ferese is accepting Wayland clients");
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
//! System wake is shared by hardware reconciliation and time-dependent UI state.
|
||||
use std::error::Error;
|
||||
use std::time::Duration;
|
||||
|
||||
use calloop::EventLoop;
|
||||
use calloop::channel::{Event, channel};
|
||||
|
||||
use crate::Ferese;
|
||||
|
||||
pub(crate) fn init(event_loop: &mut EventLoop<Ferese>) -> Result<(), Box<dyn Error>> {
|
||||
let (sender, receiver) = channel();
|
||||
event_loop.handle().insert_source(receiver, |event, _, state| {
|
||||
if let Event::Msg(()) = event {
|
||||
crate::backends::direct::system_resumed(state);
|
||||
state.refresh_theme();
|
||||
}
|
||||
})?;
|
||||
|
||||
std::thread::Builder::new()
|
||||
.name("ferese-system-resume".into())
|
||||
.spawn(move || {
|
||||
let mut warned = false;
|
||||
loop {
|
||||
let monitor = || -> zbus::Result<bool> {
|
||||
let connection = zbus::blocking::connection::Builder::system()?
|
||||
.method_timeout(Duration::from_secs(2))
|
||||
.build()?;
|
||||
let proxy = zbus::blocking::Proxy::new(
|
||||
&connection,
|
||||
"org.freedesktop.login1",
|
||||
"/org/freedesktop/login1",
|
||||
"org.freedesktop.login1.Manager",
|
||||
)?;
|
||||
for message in proxy.receive_signal("PrepareForSleep")? {
|
||||
let (sleeping,): (bool,) = message.body().deserialize()?;
|
||||
if !sleeping && sender.send(()).is_err() {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
};
|
||||
|
||||
match monitor() {
|
||||
Ok(false) => break,
|
||||
Ok(true) => warned = false,
|
||||
Err(error) if !warned => {
|
||||
tracing::warn!(%error, "system resume monitor unavailable; retrying connection");
|
||||
warned = true;
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
// Recovery only: a healthy listener blocks on DBus signals.
|
||||
std::thread::sleep(Duration::from_secs(5));
|
||||
}
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -265,32 +265,6 @@ pub(crate) fn init(
|
||||
}
|
||||
paths.lock().unwrap().insert(PathBuf::from("/etc/localtime"));
|
||||
let wakeup = state.loop_signal.clone();
|
||||
let resume_sender = sender.clone();
|
||||
let resume_wakeup = wakeup.clone();
|
||||
std::thread::Builder::new()
|
||||
.name("ferese-theme-resume".into())
|
||||
.spawn(move || {
|
||||
let monitor = || -> zbus::Result<()> {
|
||||
let connection = zbus::blocking::Connection::system()?;
|
||||
let proxy = zbus::blocking::Proxy::new(
|
||||
&connection,
|
||||
"org.freedesktop.login1",
|
||||
"/org/freedesktop/login1",
|
||||
"org.freedesktop.login1.Manager",
|
||||
)?;
|
||||
for message in proxy.receive_signal("PrepareForSleep")? {
|
||||
let (sleeping,): (bool,) = message.body().deserialize()?;
|
||||
if !sleeping {
|
||||
let _ = resume_sender.try_send(());
|
||||
resume_wakeup.wakeup();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
if let Err(error) = monitor() {
|
||||
tracing::warn!(%error, "theme resume monitor unavailable");
|
||||
}
|
||||
})?;
|
||||
let system_sender = sender.clone();
|
||||
let system_wakeup = wakeup.clone();
|
||||
std::thread::Builder::new()
|
||||
|
||||
Reference in new issue
Block a user