Skip to content

Commit

Permalink
[WIP] Draft event stream IPC
Browse files Browse the repository at this point in the history
  • Loading branch information
YaLTeR committed Jun 20, 2024
1 parent 212b262 commit f0e5bbd
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 16 deletions.
54 changes: 54 additions & 0 deletions niri-ipc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ pub enum Request {
Workspaces,
/// Request information about the focused output.
FocusedOutput,
/// Start continuously receiving events from the compositor.
///
/// The compositor should reply with `Reply::Ok(Response::Handled)`, then continuously send
/// [`Event`]s, one per line.
EventStream,
/// Respond with an error (for testing error handling).
ReturnError,
}
Expand Down Expand Up @@ -507,6 +512,10 @@ pub enum OutputConfigChanged {
/// A workspace.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Workspace {
/// Unique id of this workspace.
///
/// This id remains constant regardless of the workspace moving around and across monitors.
pub id: u64,
/// Index of the workspace on its monitor.
///
/// This is the same index you can use for requests like `niri msg action focus-workspace`.
Expand All @@ -521,6 +530,51 @@ pub struct Workspace {
pub is_active: bool,
}

/// A compositor event.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Event {
/// A new workspace was created.
WorkspaceCreated {
/// The new workspace.
workspace: Workspace,
},
/// A workspace was removed.
WorkspaceRemoved {
/// Id of the removed workspace.
id: u64,
},
/// A workspace was switched on an output.
///
/// This doesn't mean the workspace became focused, just that it's now the active workspace on
/// its output.
WorkspaceSwitched {
/// Output where the workspace was switched.
output: String,
/// Id of the newly active workspace.
id: u64,
},
/// A workspace moved on an output or to a different output.
WorkspaceMoved {
/// Id of the moved workspace.
id: u64,
/// New output of the workspace.
output: String,
/// New position of the workspace on the output.
idx: u8,
},
/// Window focus changed.
WindowFocused {
// FIXME: replace with id, and WindowCreated/Removed.
/// The newly focused window, or `None` if no window is now focused.
window: Option<Window>,
},
/// The keyboard layout changed.
KeyboardLayoutChanged {
/// Name of the newly active layout.
name: String,
},
}

impl FromStr for WorkspaceReferenceArg {
type Err = &'static str;

Expand Down
18 changes: 15 additions & 3 deletions niri-ipc/src/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::net::Shutdown;
use std::os::unix::net::UnixStream;
use std::path::Path;

use crate::{Reply, Request};
use crate::{Event, Reply, Request};

/// Name of the environment variable containing the niri IPC socket path.
pub const SOCKET_PATH_ENV: &str = "NIRI_SOCKET";
Expand Down Expand Up @@ -47,7 +47,11 @@ impl Socket {
/// * `Ok(Ok(response))`: successful [`Response`](crate::Response) from niri
/// * `Ok(Err(message))`: error message from niri
/// * `Err(error)`: error communicating with niri
pub fn send(self, request: Request) -> io::Result<Reply> {
///
/// This method also returns a blocking function that you can call to keep reading [`Event`]s
/// after requesting an [`EventStream`][Request::EventStream]. This function is not useful
/// otherwise.
pub fn send(self, request: Request) -> io::Result<(Reply, impl FnMut() -> io::Result<Event>)> {
let Self { mut stream } = self;

let mut buf = serde_json::to_string(&request).unwrap();
Expand All @@ -60,6 +64,14 @@ impl Socket {
reader.read_line(&mut buf)?;

let reply = serde_json::from_str(&buf)?;
Ok(reply)

let events = move || {
buf.clear();
reader.read_line(&mut buf)?;
let event = serde_json::from_str(&buf)?;
Ok(event)
};

Ok((reply, events))
}
}
2 changes: 2 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ pub enum Msg {
#[command(subcommand)]
action: OutputAction,
},
/// Start continuously receiving events from the compositor.
EventStream,
/// Print the version of the running niri instance.
Version,
/// Request an error from the running niri instance.
Expand Down
37 changes: 35 additions & 2 deletions src/ipc/client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::{anyhow, bail, Context};
use niri_ipc::{
LogicalOutput, Mode, Output, OutputConfigChanged, Request, Response, Socket, Transform,
Event, LogicalOutput, Mode, Output, OutputConfigChanged, Request, Response, Socket, Transform,
};
use serde_json::json;

Expand All @@ -19,12 +19,13 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
action: action.clone(),
},
Msg::Workspaces => Request::Workspaces,
Msg::EventStream => Request::EventStream,
Msg::RequestError => Request::ReturnError,
};

let socket = Socket::connect().context("error connecting to the niri socket")?;

let reply = socket
let (reply, mut read_event) = socket
.send(request)
.context("error communicating with niri")?;

Expand All @@ -35,6 +36,7 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
Socket::connect()
.and_then(|socket| socket.send(Request::Version))
.ok()
.map(|(reply, _read_event)| reply)
}
_ => None,
};
Expand Down Expand Up @@ -238,6 +240,37 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
println!("{is_active}{idx}{name}");
}
}
Msg::EventStream => {
let Response::Handled = response else {
bail!("unexpected response: expected Handled, got {response:?}");
};

println!("Started reading events.");

loop {
let event = read_event().context("error reading event from niri")?;
match event {
Event::WorkspaceCreated { workspace } => {
println!("Workspace created: {workspace:?}");
}
Event::WorkspaceRemoved { id } => {
println!("Workspace removed: {id}");
}
Event::WorkspaceSwitched { output, id } => {
println!("Workspace switched on output \"{output}\": {id}");
}
Event::WorkspaceMoved { id, output, idx } => {
println!("Workspace moved: {id} to output \"{output}\", index {idx}");
}
Event::WindowFocused { window } => {
println!("Window focused: {window:?}");
}
Event::KeyboardLayoutChanged { name } => {
println!("Keyboard layout changed: \"{name}\"");
}
}
}
}
}

Ok(())
Expand Down
Loading

0 comments on commit f0e5bbd

Please sign in to comment.