diff options
Diffstat (limited to 'src/ipc')
| -rw-r--r-- | src/ipc/client.rs | 30 | ||||
| -rw-r--r-- | src/ipc/server.rs | 25 |
2 files changed, 55 insertions, 0 deletions
diff --git a/src/ipc/client.rs b/src/ipc/client.rs index 18d49fcb..424b243d 100644 --- a/src/ipc/client.rs +++ b/src/ipc/client.rs @@ -21,6 +21,7 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> { let request = match &msg { Msg::Outputs => Request::Outputs, + Msg::FocusedWindow => Request::FocusedWindow, Msg::Action { action } => Request::Action(action.clone()), }; let mut buf = serde_json::to_vec(&request).unwrap(); @@ -147,6 +148,35 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> { println!(); } } + Msg::FocusedWindow => { + let Response::FocusedWindow(window) = response else { + bail!("unexpected response: expected FocusedWindow, got {response:?}"); + }; + + if json { + let window = serde_json::to_string(&window).context("error formatting response")?; + println!("{window}"); + return Ok(()); + } + + if let Some(window) = window { + println!("Focused window:"); + + if let Some(title) = window.title { + println!(" Title: \"{title}\""); + } else { + println!(" Title: (unset)"); + } + + if let Some(app_id) = window.app_id { + println!(" App ID: \"{app_id}\""); + } else { + println!(" App ID: (unset)"); + } + } else { + println!("No window is focused."); + } + } Msg::Action { .. } => { let Response::Handled = response else { bail!("unexpected response: expected Handled, got {response:?}"); diff --git a/src/ipc/server.rs b/src/ipc/server.rs index 43cbb8b4..38a9b3dd 100644 --- a/src/ipc/server.rs +++ b/src/ipc/server.rs @@ -9,9 +9,12 @@ use directories::BaseDirs; use futures_util::io::{AsyncReadExt, BufReader}; use futures_util::{AsyncBufReadExt, AsyncWriteExt}; use niri_ipc::{Request, Response}; +use smithay::desktop::Window; use smithay::reexports::calloop::generic::Generic; use smithay::reexports::calloop::{Interest, LoopHandle, Mode, PostAction}; use smithay::reexports::rustix::fs::unlink; +use smithay::wayland::compositor::with_states; +use smithay::wayland::shell::xdg::XdgToplevelSurfaceData; use crate::backend::IpcOutputMap; use crate::niri::State; @@ -23,6 +26,7 @@ pub struct IpcServer { struct ClientCtx { event_loop: LoopHandle<'static, State>, ipc_outputs: Arc<Mutex<IpcOutputMap>>, + ipc_focused_window: Arc<Mutex<Option<Window>>>, } impl IpcServer { @@ -87,6 +91,7 @@ fn on_new_ipc_client(state: &mut State, stream: UnixStream) { let ctx = ClientCtx { event_loop: state.niri.event_loop.clone(), ipc_outputs: state.backend.ipc_outputs(), + ipc_focused_window: state.niri.ipc_focused_window.clone(), }; let future = async move { @@ -128,6 +133,26 @@ fn process(ctx: &ClientCtx, buf: &str) -> anyhow::Result<Response> { let ipc_outputs = ctx.ipc_outputs.lock().unwrap().clone(); Response::Outputs(ipc_outputs) } + Request::FocusedWindow => { + let window = ctx.ipc_focused_window.lock().unwrap().clone(); + let window = window.map(|window| { + let wl_surface = window.toplevel().expect("no X11 support").wl_surface(); + with_states(wl_surface, |states| { + let role = states + .data_map + .get::<XdgToplevelSurfaceData>() + .unwrap() + .lock() + .unwrap(); + + niri_ipc::Window { + title: role.title.clone(), + app_id: role.app_id.clone(), + } + }) + }); + Response::FocusedWindow(window) + } Request::Action(action) => { let action = niri_config::Action::from(action); ctx.event_loop.insert_idle(move |state| { |
