diff options
| -rw-r--r-- | src/dbus/freedesktop_login1.rs | 105 | ||||
| -rw-r--r-- | src/dbus/mod.rs | 18 | ||||
| -rw-r--r-- | src/niri.rs | 12 |
3 files changed, 135 insertions, 0 deletions
diff --git a/src/dbus/freedesktop_login1.rs b/src/dbus/freedesktop_login1.rs new file mode 100644 index 00000000..5964d45b --- /dev/null +++ b/src/dbus/freedesktop_login1.rs @@ -0,0 +1,105 @@ +use futures_util::StreamExt; +use zbus::fdo; +use zbus::names::InterfaceName; + +pub enum Login1ToNiri { + LidClosedChanged(bool), +} + +pub fn start( + to_niri: calloop::channel::Sender<Login1ToNiri>, +) -> anyhow::Result<zbus::blocking::Connection> { + let conn = zbus::blocking::Connection::system()?; + + let async_conn = conn.inner().clone(); + let future = async move { + let proxy = fdo::PropertiesProxy::new( + &async_conn, + "org.freedesktop.login1", + "/org/freedesktop/login1", + ) + .await; + let proxy = match proxy { + Ok(x) => x, + Err(err) => { + warn!("error creating PropertiesProxy: {err:?}"); + return; + } + }; + + let mut props_changed = match proxy.receive_properties_changed().await { + Ok(x) => x, + Err(err) => { + warn!("error subscribing to PropertiesChanged: {err:?}"); + return; + } + }; + + let props = proxy + .get_all(InterfaceName::try_from("org.freedesktop.login1.Manager").unwrap()) + .await; + let mut props = match props { + Ok(x) => x, + Err(err) => { + warn!("error receiving initial properties: {err:?}"); + return; + } + }; + + trace!("initial properties: {props:?}"); + + let mut lid_closed = props + .remove("LidClosed") + .and_then(|value| bool::try_from(value).ok()) + .unwrap_or_default(); + + if let Err(err) = to_niri.send(Login1ToNiri::LidClosedChanged(lid_closed)) { + warn!("error sending initial lid state to niri: {err:?}"); + return; + }; + + while let Some(signal) = props_changed.next().await { + let args = match signal.args() { + Ok(args) => args, + Err(err) => { + warn!("error parsing PropertiesChanged args: {err:?}"); + return; + } + }; + + let mut new_lid_closed = lid_closed; + let mut changed = false; + for (name, value) in args.changed_properties() { + trace!("changed property: {name} => {value:?}"); + if *name != "LidClosed" { + continue; + } + + new_lid_closed = bool::try_from(value).unwrap_or(new_lid_closed); + changed = true; + } + + if !changed { + continue; + } + + if new_lid_closed == lid_closed { + continue; + } + + lid_closed = new_lid_closed; + if let Err(err) = to_niri.send(Login1ToNiri::LidClosedChanged(lid_closed)) { + warn!("error sending message to niri: {err:?}"); + return; + }; + } + }; + + let task = conn + .inner() + .executor() + .spawn(future, "monitor login1 property changes"); + task.detach(); + + Ok(conn) +} diff --git a/src/dbus/mod.rs b/src/dbus/mod.rs index 6a164d48..5941456f 100644 --- a/src/dbus/mod.rs +++ b/src/dbus/mod.rs @@ -5,6 +5,7 @@ use crate::niri::State; pub mod freedesktop_a11y; pub mod freedesktop_locale1; +pub mod freedesktop_login1; pub mod freedesktop_screensaver; pub mod gnome_shell_introspect; pub mod gnome_shell_screenshot; @@ -35,6 +36,7 @@ pub struct DBusServers { pub conn_introspect: Option<Connection>, #[cfg(feature = "xdp-gnome-screencast")] pub conn_screen_cast: Option<Connection>, + pub conn_login1: Option<Connection>, pub conn_locale1: Option<Connection>, pub conn_keyboard_monitor: Option<Connection>, } @@ -136,6 +138,22 @@ impl DBusServers { } } + let (to_niri, from_login1) = calloop::channel::channel(); + niri.event_loop + .insert_source(from_login1, move |event, _, state| match event { + calloop::channel::Event::Msg(msg) => state.on_login1_msg(msg), + calloop::channel::Event::Closed => (), + }) + .unwrap(); + match freedesktop_login1::start(to_niri) { + Ok(conn) => { + dbus.conn_login1 = Some(conn); + } + Err(err) => { + warn!("error starting login1 watcher: {err:?}"); + } + } + let (to_niri, from_locale1) = calloop::channel::channel(); niri.event_loop .insert_source(from_locale1, move |event, _, state| match event { diff --git a/src/niri.rs b/src/niri.rs index d5c6a8b5..3ced8a90 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -120,6 +120,8 @@ use crate::cursor::{CursorManager, CursorTextureCache, RenderCursor, XCursor}; #[cfg(feature = "dbus")] use crate::dbus::freedesktop_locale1::Locale1ToNiri; #[cfg(feature = "dbus")] +use crate::dbus::freedesktop_login1::Login1ToNiri; +#[cfg(feature = "dbus")] use crate::dbus::gnome_shell_introspect::{self, IntrospectToNiri, NiriToIntrospect}; #[cfg(feature = "dbus")] use crate::dbus::gnome_shell_screenshot::{NiriToScreenshot, ScreenshotToNiri}; @@ -726,6 +728,8 @@ impl State { self.niri.notified_activity_this_iteration = false; } + // We monitor both libinput and logind: libinput is always there (including without DBus), but + // it misses some switch events (e.g. after unsuspend) on some systems. pub fn set_lid_closed(&mut self, is_closed: bool) { if self.niri.is_lid_closed == is_closed { return; @@ -2299,6 +2303,14 @@ impl State { } #[cfg(feature = "dbus")] + pub fn on_login1_msg(&mut self, msg: Login1ToNiri) { + let Login1ToNiri::LidClosedChanged(is_closed) = msg; + + trace!("login1 lid {}", if is_closed { "closed" } else { "opened" }); + self.set_lid_closed(is_closed); + } + + #[cfg(feature = "dbus")] pub fn on_locale1_msg(&mut self, msg: Locale1ToNiri) { let Locale1ToNiri::XkbChanged(xkb) = msg; |
