aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--niri-ipc/src/lib.rs22
-rw-r--r--src/ipc/client.rs27
-rw-r--r--src/ipc/server.rs24
3 files changed, 45 insertions, 28 deletions
diff --git a/niri-ipc/src/lib.rs b/niri-ipc/src/lib.rs
index 5131388f..622435f4 100644
--- a/niri-ipc/src/lib.rs
+++ b/niri-ipc/src/lib.rs
@@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
pub const SOCKET_PATH_ENV: &str = "NIRI_SOCKET";
/// Request from client to niri.
-#[derive(Debug, Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum Request {
/// Request information about connected outputs.
Outputs,
@@ -18,9 +18,21 @@ pub enum Request {
Action(Action),
}
-/// Response from niri to client.
-#[derive(Debug, Serialize, Deserialize)]
+/// 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<Response, String>;
+
+/// Successful response from niri to client.
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum Response {
+ /// A request that does not need a response was handled successfully.
+ Handled,
/// Information about connected outputs.
///
/// Map from connector name to output info.
@@ -30,7 +42,7 @@ pub enum Response {
/// Actions that niri can perform.
// Variants in this enum should match the spelling of the ones in niri-config. Most, but not all,
// variants from niri-config should be present here.
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
#[cfg_attr(feature = "clap", derive(clap::Parser))]
#[cfg_attr(feature = "clap", command(subcommand_value_name = "ACTION"))]
#[cfg_attr(feature = "clap", command(subcommand_help_heading = "Actions"))]
@@ -205,7 +217,7 @@ pub enum SizeChange {
}
/// Layout to switch to.
-#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
+#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum LayoutSwitchTarget {
/// The next configured layout.
Next,
diff --git a/src/ipc/client.rs b/src/ipc/client.rs
index b2004a7b..27e1eaa1 100644
--- a/src/ipc/client.rs
+++ b/src/ipc/client.rs
@@ -3,8 +3,8 @@ use std::io::{Read, Write};
use std::net::Shutdown;
use std::os::unix::net::UnixStream;
-use anyhow::{bail, Context};
-use niri_ipc::{Mode, Output, Request, Response};
+use anyhow::{anyhow, bail, Context};
+use niri_ipc::{Mode, Output, Reply, Request, Response};
use crate::cli::Msg;
@@ -36,20 +36,15 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
.read_to_end(&mut buf)
.context("error reading IPC response")?;
- if matches!(msg, Msg::Action { .. }) {
- if buf.is_empty() {
- return Ok(());
- } else {
- bail!("unexpected response: expected no response, got {buf:?}");
- }
- }
+ let reply: Reply = serde_json::from_slice(&buf).context("error parsing IPC reply")?;
+
+ let response = reply
+ .map_err(|msg| anyhow!(msg))
+ .context("niri could not handle the request")?;
- let response = serde_json::from_slice(&buf).context("error parsing IPC response")?;
match msg {
Msg::Outputs => {
- #[allow(irrefutable_let_patterns)]
- let Response::Outputs(outputs) = response
- else {
+ let Response::Outputs(outputs) = response else {
bail!("unexpected response: expected Outputs, got {response:?}");
};
@@ -109,7 +104,11 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
println!();
}
}
- Msg::Action { .. } => unreachable!(),
+ Msg::Action { .. } => {
+ let Response::Handled = response else {
+ bail!("unexpected response: expected Handled, got {response:?}");
+ };
+ }
}
Ok(())
diff --git a/src/ipc/server.rs b/src/ipc/server.rs
index bedfe48f..a1f3905b 100644
--- a/src/ipc/server.rs
+++ b/src/ipc/server.rs
@@ -110,7 +110,19 @@ async fn handle_client(ctx: ClientCtx, stream: Async<'_, UnixStream>) -> anyhow:
.await
.context("error reading request")?;
- let request: Request = serde_json::from_str(&buf).context("error parsing request")?;
+ let reply = process(&ctx, &buf).map_err(|err| {
+ warn!("error processing IPC request: {err:?}");
+ err.to_string()
+ });
+
+ let buf = serde_json::to_vec(&reply).context("error formatting reply")?;
+ write.write_all(&buf).await.context("error writing reply")?;
+
+ Ok(())
+}
+
+fn process(ctx: &ClientCtx, buf: &str) -> anyhow::Result<Response> {
+ let request: Request = serde_json::from_str(buf).context("error parsing request")?;
let response = match request {
Request::Outputs => {
@@ -122,15 +134,9 @@ async fn handle_client(ctx: ClientCtx, stream: Async<'_, UnixStream>) -> anyhow:
ctx.event_loop.insert_idle(move |state| {
state.do_action(action);
});
- return Ok(());
+ Response::Handled
}
};
- let buf = serde_json::to_vec(&response).context("error formatting response")?;
- write
- .write_all(&buf)
- .await
- .context("error writing response")?;
-
- Ok(())
+ Ok(response)
}