Disable/reenable DPMS when inhibiting

This commit is contained in:
Brian Tarricone 2022-08-15 22:54:54 -07:00
parent 0ef979549a
commit 4c8ffbab6a
2 changed files with 108 additions and 14 deletions

View File

@ -11,5 +11,5 @@ bscreensaver-command = { path = "../command" }
bscreensaver-util = { path = "../util" } bscreensaver-util = { path = "../util" }
futures = "0.3" futures = "0.3"
log = "0.4" log = "0.4"
xcb = "1" xcb = { version = "1", features = ["dpms"] }
zbus = "2" zbus = "2"

View File

@ -79,6 +79,93 @@ impl ScreenSaver {
} }
} }
#[derive(Clone, Copy, PartialEq)]
enum DpmsState {
Unknown,
WasDisabled,
WeDisabled,
WeEnabled,
}
struct DpmsHandling {
conn: Option<xcb::Connection>,
state: DpmsState,
}
impl DpmsHandling {
fn init() -> Self {
xcb::Connection::connect_with_extensions(None, &[xcb::Extension::Dpms],&[])
.map(|(conn, _)| Some(conn))
.unwrap_or_else(|err| match err {
xcb::ConnError::ClosedExtNotSupported => None,
_ => {
warn!("Failed to connect to X display; we will not handle DPMS on inhibit: {}", err);
None
},
})
.and_then(|conn| {
let cookie = conn.send_request(&xcb::dpms::Capable {});
conn.wait_for_reply(cookie)
.map(|capable| {
if capable.capable() {
Some(Self {
conn: Some(conn),
state: DpmsState::Unknown,
})
} else {
None
}
})
.unwrap_or_else(|err| {
warn!("Failed to check if X server is DPMS-capable: {}", err);
None
})
})
.unwrap_or_else(|| Self {
conn: None,
state: DpmsState::Unknown,
})
}
fn maybe_disable_dpms(&mut self) {
if let Some(conn) = &self.conn {
let cookie = conn.send_request(&xcb::dpms::Info {});
match conn.wait_for_reply(cookie) {
Err(err) => warn!("Failed to query DPMS state: {}", err),
Ok(info) if info.state() => match conn.send_and_check_request(&xcb::dpms::Disable {}) {
Err(err) => warn!("Failed to disable DPMS: {}", err),
Ok(_) => {
debug!("Successfully disabled DPMS");
self.state = DpmsState::WeDisabled;
},
}
Ok(_) => {
debug!("DPMS was already disabled");
if self.state != DpmsState::WeDisabled {
self.state = DpmsState::WasDisabled;
}
},
}
}
}
fn maybe_enable_dpms(&mut self) {
if let Some(conn) = &self.conn {
if self.state == DpmsState::WeDisabled {
match conn.send_and_check_request(&xcb::dpms::Enable {}) {
Err(err) => warn!("Failed to enable DPMS: {}", err),
Ok(_) => {
debug!("Successfully enabled DPMS");
self.state = DpmsState::WeEnabled;
},
}
} else {
debug!("We didn't disable DPMS, so we're not going to re-enable it");
}
}
}
}
#[async_std::main] #[async_std::main]
async fn main() { async fn main() {
init_logging("BSCREENSAVER_DBUS_SERVICE_LOG"); init_logging("BSCREENSAVER_DBUS_SERVICE_LOG");
@ -183,6 +270,7 @@ async fn dbus_task(state: Arc<Mutex<State>>) -> anyhow::Result<()> {
async fn heartbeat_task(state_mtx: Arc<Mutex<State>>) -> anyhow::Result<()> { async fn heartbeat_task(state_mtx: Arc<Mutex<State>>) -> anyhow::Result<()> {
let mut last_heartbeat: Option<Instant> = None; let mut last_heartbeat: Option<Instant> = None;
let mut dpms_handling = task::block_on(async { DpmsHandling::init() });
loop { loop {
let state = state_mtx.lock().await; let state = state_mtx.lock().await;
@ -207,7 +295,10 @@ async fn heartbeat_task(state_mtx: Arc<Mutex<State>>) -> anyhow::Result<()> {
debug!("Heartbeat timeout expired"); debug!("Heartbeat timeout expired");
let state = state_mtx.lock().await; let state = state_mtx.lock().await;
if !state.inhibitors.is_empty() && (last_heartbeat.is_none() || last_heartbeat.as_ref().filter(|lh| lh.elapsed() < HEARTBEAT_INTERVAL).is_none()) { if !state.inhibitors.is_empty() {
task::block_on(async { dpms_handling.maybe_disable_dpms() });
if last_heartbeat.is_none() || last_heartbeat.as_ref().filter(|lh| lh.elapsed() < HEARTBEAT_INTERVAL).is_none() {
trace!("About to deactivate; active inhibitors:"); trace!("About to deactivate; active inhibitors:");
for inhibitor in &state.inhibitors { for inhibitor in &state.inhibitors {
trace!(" {}: {}", inhibitor.cookie, inhibitor.app_name); trace!(" {}: {}", inhibitor.cookie, inhibitor.app_name);
@ -222,6 +313,9 @@ async fn heartbeat_task(state_mtx: Arc<Mutex<State>>) -> anyhow::Result<()> {
} }
}); });
} }
} else {
task::block_on(async { dpms_handling.maybe_enable_dpms() });
}
} }
} }