aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2024-06-26 10:08:07 +0400
committerIvan Molodetskikh <yalterz@gmail.com>2024-06-28 10:39:35 +0400
commit40aff3a094756b2cfa9c0ce48c1ab2be26b1f9ee (patch)
tree662ddc0e7ecb92bc01e025d7f7c54a0522e91d9e /src
parent6c5f10035a24963336d99f801164e7437ebffa77 (diff)
downloadniri-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.rs81
-rw-r--r--src/dbus/mod.rs16
-rw-r--r--src/niri.rs46
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 {