Prevent screen turning off during playback
Closes: #157 XDG portals expose a D-Bus interface allowing apps to prevent screen idling, user switching, and other actions. That interface, org.freedesktop.portal.Inhibit, does all of the heavy lifting here. Idling = the screen dimming or shutting off. Idling is inhibited when a video is actively playing. Idling is NOT inhibited when videos aren't playing - this includes paused or stopped videos.
This commit is contained in:
parent
fa1637fe51
commit
bb087578df
10 changed files with 331 additions and 40 deletions
61
src/xdg_portals.rs
Normal file
61
src/xdg_portals.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2023 System76 <info@system76.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
//! Integrations with XDG portals.
|
||||
|
||||
use ashpd::{
|
||||
desktop::inhibit::{InhibitFlags, InhibitProxy},
|
||||
enumflags2::{BitFlags, make_bitflags},
|
||||
};
|
||||
use log::{debug, warn};
|
||||
use tokio::sync::watch::Receiver;
|
||||
|
||||
// Actions to inhibit. Currently, COSMIC defaults to the GTK portal for Inhibit. That
|
||||
// implementation only supports inhibiting idling and trying to inhibit anything else causes the
|
||||
// D-Bus call to silently fail. We will only inhibit idling until COSMIC gets a bespoke Inhibit.
|
||||
const INHIBIT_FLAGS: BitFlags<InhibitFlags> = make_bitflags!(InhibitFlags::{Idle});
|
||||
|
||||
/// Inhibit idle and user switching while media is played.
|
||||
///
|
||||
/// # Usage
|
||||
/// Enable the inhibitor by setting the watcher to `true`. Disable the inhibitor by sending a
|
||||
/// `false`. Sending multiple consecutive trues/falses is safe and guarded internally.
|
||||
///
|
||||
/// Portal:
|
||||
/// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Inhibit.html
|
||||
pub async fn inhibit(mut signal: Receiver<bool>) -> ashpd::Result<()> {
|
||||
let proxy = InhibitProxy::new().await?;
|
||||
// Mark the watcher's value as unseen so a temporary or mutable bool isn't needed in the loop.
|
||||
signal.mark_changed();
|
||||
|
||||
loop {
|
||||
if signal.wait_for(|&status| status).await.is_err() {
|
||||
// The watcher will likely only be closed when the app is closed.
|
||||
debug!("Inhibit task's watcher is closed");
|
||||
break;
|
||||
}
|
||||
// Copying the bool is important or else we would needlessly hold the lock below.
|
||||
let should_inhibit = *signal.borrow_and_update();
|
||||
|
||||
if should_inhibit
|
||||
&& let Some(inhibit_handle) = proxy
|
||||
.inhibit(None, INHIBIT_FLAGS, "")
|
||||
.await
|
||||
.inspect_err(|e| warn!("Failed to call inhibit portal endpoint: {e}"))
|
||||
.ok()
|
||||
{
|
||||
// We don't have to check the bool because it's already checked to be false in the
|
||||
// closure. We also don't have to break on error because the next iteration of the loop
|
||||
// would break anyway.
|
||||
let _ = signal.wait_for(|&status| !status).await;
|
||||
if let Err(e) = inhibit_handle.close().await {
|
||||
// This should only happen if the inhibit portal silently fails which GTK (and
|
||||
// others!) apparently do.
|
||||
warn!("Removing the inhibitor failed: {e}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue