//! Types for communicating with niri via IPC.
//!
//! After connecting to the niri socket, you can send [`Request`]s. Niri will process them one by
//! one, in order, and to each request it will respond with a single [`Reply`], which is a `Result`
//! wrapping a [`Response`].
//!
//! If you send a [`Request::EventStream`], niri will *stop* reading subsequent [`Request`]s, and
//! will start continuously writing compositor [`Event`]s to the socket. If you'd like to read an
//! event stream and write more requests at the same time, you need to use two IPC sockets.
//!
//!
//!
//! Requests are *always* processed separately. Time passes between requests, even when sending
//! multiple requests to the socket at once. For example, sending [`Request::Workspaces`] and
//! [`Request::Windows`] together may not return consistent results (e.g. a window may open on a
//! new workspace in-between the two responses). This goes for actions too: sending
//! [`Action::FocusWindow`] and [Action::CloseWindow] { id: None } together may close
//! the wrong window because a different window got focused in-between these requests.
//!
//!
//!
//! You can use the [`socket::Socket`] helper if you're fine with blocking communication. However,
//! it is a fairly simple helper, so if you need async, or if you're using a different language,
//! you are encouraged to communicate with the socket manually.
//!
//! 1. Read the socket filesystem path from [`socket::SOCKET_PATH_ENV`] (`$NIRI_SOCKET`).
//! 2. Connect to the socket and write a JSON-formatted [`Request`] on a single line. You can follow
//! up with a line break and a flush, or just flush and shutdown the write end of the socket.
//! 3. Niri will respond with a single line JSON-formatted [`Reply`].
//! 4. You can keep writing [`Request`]s, each on a single line, and read [`Reply`]s, also each on a
//! separate line.
//! 5. After you request an event stream, niri will keep responding with JSON-formatted [`Event`]s,
//! on a single line each.
//!
//! ## Backwards compatibility
//!
//! This crate follows the niri version. It is **not** API-stable in terms of the Rust semver. In
//! particular, expect new struct fields and enum variants to be added in patch version bumps.
//!
//! Use an exact version requirement to avoid breaking changes:
//!
//! ```toml
//! [dependencies]
//! niri-ipc = "=25.5.1"
//! ```
//!
//! ## Features
//!
//! This crate defines the following features:
//! - `json-schema`: derives the [schemars](https://lib.rs/crates/schemars) `JsonSchema` trait for
//! the types.
//! - `clap`: derives the clap CLI parsing traits for some types. Used internally by niri itself.
#![warn(missing_docs)]
use std::collections::HashMap;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
pub mod socket;
pub mod state;
/// Request from client to niri.
#[derive(Debug, Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub enum Request {
/// Request the version string for the running niri instance.
Version,
/// Request information about connected outputs.
Outputs,
/// Request information about workspaces.
Workspaces,
/// Request information about open windows.
Windows,
/// Request information about layer-shell surfaces.
Layers,
/// Request information about the configured keyboard layouts.
KeyboardLayouts,
/// Request information about the focused output.
FocusedOutput,
/// Request information about the focused window.
FocusedWindow,
/// Request picking a window and get its information.
PickWindow,
/// Request picking a color from the screen.
PickColor,
/// Perform an action.
Action(Action),
/// Change output configuration temporarily.
///
/// The configuration is changed temporarily and not saved into the config file. If the output
/// configuration subsequently changes in the config file, these temporary changes will be
/// forgotten.
Output {
/// Output name.
output: String,
/// Configuration to apply.
action: OutputAction,
},
/// Start continuously receiving events from the compositor.
///
/// The compositor should reply with `Reply::Ok(Response::Handled)`, then continuously send
/// [`Event`]s, one per line.
///
/// The event stream will always give you the full current state up-front. For example, the
/// first workspace-related event you will receive will be [`Event::WorkspacesChanged`]
/// containing the full current workspaces state. You *do not* need to separately send
/// [`Request::Workspaces`] when using the event stream.
///
/// Where reasonable, event stream state updates are atomic, though this is not always the
/// case. For example, a window may end up with a workspace id for a workspace that had already
/// been removed. This can happen if the corresponding [`Event::WorkspacesChanged`] arrives
/// before the corresponding [`Event::WindowOpenedOrChanged`].
EventStream,
/// Respond with an error (for testing error handling).
ReturnError,
/// Request information about the overview.
OverviewState,
}
/// Reply from niri to client.
///
/// Every request gets one reply.
///
/// * If an error had occurred, it will be an `Reply::Err`.
/// * If the request does not need any particular response, it will be
/// `Reply::Ok(Response::Handled)`. Kind of like an `Ok(())`.
/// * Otherwise, it will be `Reply::Ok(response)` with one of the other [`Response`] variants.
pub type Reply = Result;
/// Successful response from niri to client.
#[derive(Debug, Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub enum Response {
/// A request that does not need a response was handled successfully.
Handled,
/// The version string for the running niri instance.
Version(String),
/// Information about connected outputs.
///
/// Map from output name to output info.
Outputs(HashMap),
/// Information about workspaces.
Workspaces(Vec),
/// Information about open windows.
Windows(Vec),
/// Information about layer-shell surfaces.
Layers(Vec),
/// Information about the keyboard layout.
KeyboardLayouts(KeyboardLayouts),
/// Information about the focused output.
FocusedOutput(Option