diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-06-26 10:08:07 +0400 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-06-28 10:39:35 +0400 |
| commit | 40aff3a094756b2cfa9c0ce48c1ab2be26b1f9ee (patch) | |
| tree | 662ddc0e7ecb92bc01e025d7f7c54a0522e91d9e /src | |
| parent | 6c5f10035a24963336d99f801164e7437ebffa77 (diff) | |
| download | niri-40aff3a094756b2cfa9c0ce48c1ab2be26b1f9ee.tar.gz niri-40aff3a094756b2cfa9c0ce48c1ab2be26b1f9ee.tar.bz2 niri-40aff3a094756b2cfa9c0ce48c1ab2be26b1f9ee.zip | |
Implement org/gnome/shell/Introspect/GetWindows
Diffstat (limited to 'src')
| -rw-r--r-- | src/dbus/gnome_shell_introspect.rs | 81 | ||||
| -rw-r--r-- | src/dbus/mod.rs | 16 | ||||
| -rw-r--r-- | src/niri.rs | 46 |
3 files changed, 143 insertions, 0 deletions
diff --git a/src/dbus/gnome_shell_introspect.rs b/src/dbus/gnome_shell_introspect.rs new file mode 100644 index 00000000..d9485d81 --- /dev/null +++ b/src/dbus/gnome_shell_introspect.rs @@ -0,0 +1,81 @@ +use std::collections::HashMap; + +use zbus::fdo::{self, RequestNameFlags}; +use zbus::zvariant::{SerializeDict, Type, Value}; +use zbus::{dbus_interface, SignalContext}; + +use super::Start; + +pub struct Introspect { + to_niri: calloop::channel::Sender<IntrospectToNiri>, + from_niri: async_channel::Receiver<NiriToIntrospect>, +} + +pub enum IntrospectToNiri { + GetWindows, +} + +pub enum NiriToIntrospect { + Windows(HashMap<u64, WindowProperties>), +} + +#[derive(Debug, SerializeDict, Type, Value)] +#[zvariant(signature = "dict")] +pub struct WindowProperties { + /// Window title. + pub title: String, + /// Window app ID. + /// + /// This is actually the name of the .desktop file, and Shell does internal tracking to match + /// Wayland app IDs to desktop files. We don't do that yet, which is the reason why + /// xdg-desktop-portal-gnome's window list is missing icons. + #[zvariant(rename = "app-id")] + pub app_id: String, +} + +#[dbus_interface(name = "org.gnome.Shell.Introspect")] +impl Introspect { + async fn get_windows(&self) -> fdo::Result<HashMap<u64, WindowProperties>> { + if let Err(err) = self.to_niri.send(IntrospectToNiri::GetWindows) { + warn!("error sending message to niri: {err:?}"); + return Err(fdo::Error::Failed("internal error".to_owned())); + } + + match self.from_niri.recv().await { + Ok(NiriToIntrospect::Windows(windows)) => Ok(windows), + Err(err) => { + warn!("error receiving message from niri: {err:?}"); + Err(fdo::Error::Failed("internal error".to_owned())) + } + } + } + + // FIXME: call this upon window changes, once more of the infrastructure is there (will be + // needed for the event stream IPC anyway). + #[dbus_interface(signal)] + pub async fn windows_changed(ctxt: &SignalContext<'_>) -> zbus::Result<()>; +} + +impl Introspect { + pub fn new( + to_niri: calloop::channel::Sender<IntrospectToNiri>, + from_niri: async_channel::Receiver<NiriToIntrospect>, + ) -> Self { + Self { to_niri, from_niri } + } +} + +impl Start for Introspect { + fn start(self) -> anyhow::Result<zbus::blocking::Connection> { + let conn = zbus::blocking::Connection::session()?; + let flags = RequestNameFlags::AllowReplacement + | RequestNameFlags::ReplaceExisting + | RequestNameFlags::DoNotQueue; + + conn.object_server() + .at("/org/gnome/Shell/Introspect", self)?; + conn.request_name_with_flags("org.gnome.Shell.Introspect", flags)?; + + Ok(conn) + } +} diff --git a/src/dbus/mod.rs b/src/dbus/mod.rs index 7d63682d..179f10a7 100644 --- a/src/dbus/mod.rs +++ b/src/dbus/mod.rs @@ -4,6 +4,7 @@ use zbus::Interface; use crate::niri::State; pub mod freedesktop_screensaver; +pub mod gnome_shell_introspect; pub mod gnome_shell_screenshot; pub mod mutter_display_config; pub mod mutter_service_channel; @@ -14,6 +15,7 @@ pub mod mutter_screen_cast; use mutter_screen_cast::ScreenCast; use self::freedesktop_screensaver::ScreenSaver; +use self::gnome_shell_introspect::Introspect; use self::mutter_display_config::DisplayConfig; use self::mutter_service_channel::ServiceChannel; @@ -27,6 +29,7 @@ pub struct DBusServers { pub conn_display_config: Option<Connection>, pub conn_screen_saver: Option<Connection>, pub conn_screen_shot: Option<Connection>, + pub conn_introspect: Option<Connection>, #[cfg(feature = "xdp-gnome-screencast")] pub conn_screen_cast: Option<Connection>, } @@ -66,6 +69,19 @@ impl DBusServers { let screenshot = gnome_shell_screenshot::Screenshot::new(to_niri, from_niri); dbus.conn_screen_shot = try_start(screenshot); + let (to_niri, from_introspect) = calloop::channel::channel(); + let (to_introspect, from_niri) = async_channel::unbounded(); + niri.event_loop + .insert_source(from_introspect, move |event, _, state| match event { + calloop::channel::Event::Msg(msg) => { + state.on_introspect_msg(&to_introspect, msg) + } + calloop::channel::Event::Closed => (), + }) + .unwrap(); + let introspect = Introspect::new(to_niri, from_niri); + dbus.conn_introspect = try_start(introspect); + #[cfg(feature = "xdp-gnome-screencast")] if niri.pipewire.is_some() { let (to_niri, from_screen_cast) = calloop::channel::channel(); diff --git a/src/niri.rs b/src/niri.rs index 13e8b963..6767faaa 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -99,6 +99,8 @@ use crate::backend::tty::SurfaceDmabufFeedback; use crate::backend::{Backend, RenderResult, Tty, Winit}; use crate::cursor::{CursorManager, CursorTextureCache, RenderCursor, XCursor}; #[cfg(feature = "dbus")] +use crate::dbus::gnome_shell_introspect::{self, IntrospectToNiri, NiriToIntrospect}; +#[cfg(feature = "dbus")] use crate::dbus::gnome_shell_screenshot::{NiriToScreenshot, ScreenshotToNiri}; #[cfg(feature = "xdp-gnome-screencast")] use crate::dbus::mutter_screen_cast::{self, ScreenCastToNiri}; @@ -1273,6 +1275,50 @@ impl State { } } } + + #[cfg(feature = "dbus")] + pub fn on_introspect_msg( + &mut self, + to_introspect: &async_channel::Sender<NiriToIntrospect>, + msg: IntrospectToNiri, + ) { + use smithay::wayland::shell::xdg::XdgToplevelSurfaceData; + + let IntrospectToNiri::GetWindows = msg; + let _span = tracy_client::span!("GetWindows"); + + let mut windows = HashMap::new(); + + self.niri.layout.with_windows(|mapped, _| { + let wl_surface = mapped + .window + .toplevel() + .expect("no X11 support") + .wl_surface(); + + let id = u64::from(mapped.id().get()); + let props = with_states(wl_surface, |states| { + let role = states + .data_map + .get::<XdgToplevelSurfaceData>() + .unwrap() + .lock() + .unwrap(); + + gnome_shell_introspect::WindowProperties { + title: role.title.clone().unwrap_or_default(), + app_id: role.app_id.clone().unwrap_or_default(), + } + }); + + windows.insert(id, props); + }); + + let msg = NiriToIntrospect::Windows(windows); + if let Err(err) = to_introspect.send_blocking(msg) { + warn!("error sending windows to introspect: {err:?}"); + } + } } impl Niri { |
