1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
#![doc(html_root_url = "https://docs.rs/tokio-i3ipc/0.16.0")]
//! # tokio-i3ipc
//!
//! This crate provides types and functions for working with i3's IPC protocol
//! in an async context and with tokio. It re-exports the subcrate `i3ipc-types`
//! because it is also used for a synchronous implementation of the protocol.
//!
//! This library follows a similar API to the synchronous version. All important
//! functions live on the [I3](struct.I3.html) type. You must first `await` a
//! [connect](struct.I3.html#method.connect) call, then you can execute
//! commands, send/read messages from i3, or subscribe to listen to `Event`s.
//!
//! ## Subscribe & Listen
//!
//! ```no_run
//! # use tokio_stream::StreamExt;
//! # use std::io;
//! use tokio_i3ipc::{event::{Event,Subscribe}, I3};
//!
//! #[tokio::main(flavor = "current_thread")]
//! async fn main() -> io::Result<()> {
//! let mut i3 = I3::connect().await?;
//! i3.subscribe([Subscribe::Window]).await?;
//!
//! let mut listener = i3.listen();
//! while let Some(event) = listener.next().await {
//! match event? {
//! Event::Workspace(ev) => println!("workspace change event {:?}", ev),
//! Event::Window(ev) => println!("window event {:?}", ev),
//! Event::Output(ev) => println!("output event {:?}", ev),
//! Event::Mode(ev) => println!("mode event {:?}", ev),
//! Event::BarConfig(ev) => println!("bar config update {:?}", ev),
//! Event::Binding(ev) => println!("binding event {:?}", ev),
//! Event::Shutdown(ev) => println!("shutdown event {:?}", ev),
//! Event::Tick(ev) => println!("tick event {:?}", ev),
//! }
//! }
//! Ok(())
//! }
//! ```
//!
//! ## Sending/Reading from I3
//!
//! To [send messages](https://i3wm.org/docs/ipc.html#_sending_messages_to_i3) to i3,
//! call any of the `get_*` functions on [I3](struct.I3.html).
//!
//! ```no_run
//! use std::io;
//!
//! use tokio_i3ipc::{reply, I3};
//!
//! #[tokio::main(flavor = "current_thread")]
//! async fn main() -> io::Result<()> {
//! let mut i3 = I3::connect().await?;
//! // this type can be inferred, here is written explicitly:
//! let worksp: reply::Workspaces = i3.get_workspaces().await?;
//! println!("{:#?}", worksp);
//!
//! Ok(())
//! }
//! ```
//!
//! All the `get_*` functions on [I3](struct.I3.html) are simple wrappers around
//! two main async functions. You could write any of them yourself, in fact:
//! ```no_run
//! # use std::io;
//! use tokio_i3ipc::{msg, reply, MsgResponse, I3};
//!
//! #[tokio::main(flavor = "current_thread")]
//! # async fn main() -> io::Result<()> {
//! let mut i3 = I3::connect().await?;
//! // send msg RunCommand with a payload
//! let payload = "some_command";
//! i3.send_msg_body(msg::Msg::RunCommand, payload).await?;
//! let resp: MsgResponse<Vec<reply::Success>> = i3.read_msg().await?;
//! # Ok(())
//! # }
//! ```
pub use i3ipc_types::*;
pub mod codec;
mod util;
pub use util::*;
use serde::de::DeserializeOwned;
use std::io;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::UnixStream,
};
use tokio_util::codec::FramedRead;
/// Newtype wrapper for `UnixStream` that implements i3's IPC
#[derive(Debug)]
pub struct I3 {
stream: UnixStream,
}
// Implement `Future` for [I3](struct.I3.html) so it can be polled into a ready
// `UnixStream`
impl I3 {
/// Sends a message and payload, used for `get_*` commands and `run_command`
async fn _send_msg<P>(&mut self, msg: msg::Msg, payload: Option<P>) -> io::Result<()>
where
P: AsRef<str>,
{
let buf = self.stream._encode_msg(msg, payload);
self.stream.write_all(&buf).await
}
async fn _decode_msg(&mut self) -> io::Result<(u32, Vec<u8>)> {
let mut init = [0_u8; 14];
let _len = self.stream.read_exact(&mut init).await?;
assert!(&init[0..6] == MAGIC.as_bytes(), "Magic str not received");
let payload_len = u32::from_ne_bytes([init[6], init[7], init[8], init[9]]) as usize;
let msg_type = u32::from_ne_bytes([init[10], init[11], init[12], init[13]]);
let mut payload = vec![0_u8; payload_len];
let _len_read = self.stream.read_exact(&mut payload).await?;
Ok((msg_type, payload))
}
/// Connects to I3 over `UnixStream`
pub async fn connect() -> io::Result<Self> {
Ok(I3 {
stream: UnixStream::connect(socket_path()?).await?,
})
}
pub async fn send_msg_body<P>(&mut self, msg: msg::Msg, payload: P) -> io::Result<()>
where
P: AsRef<str>,
{
self._send_msg(msg, Some(payload)).await
}
pub async fn send_msg(&mut self, msg: msg::Msg) -> io::Result<()> {
self._send_msg::<&str>(msg, None).await
}
/// Receive some message from the socket. Holds a `Msg` type and payload
pub async fn read_msg<D>(&mut self) -> io::Result<MsgResponse<D>>
where
D: DeserializeOwned,
{
let (msg_type, payload) = self._decode_msg().await?;
Ok(MsgResponse {
msg_type: msg_type.into(),
body: serde_json::from_slice(&payload[..])?,
})
}
/// Like `read_msg` but for `event::Event`
pub async fn read_event(&mut self) -> io::Result<event::Event> {
let (evt_type, payload_bytes) = self._decode_msg().await?;
decode_event(evt_type, payload_bytes)
}
/// Send a `Msg` and payload and receive a response. Convenience function
/// over `send_msg` and `read_msg`
pub async fn send_read<P, D>(&mut self, msg: msg::Msg, payload: P) -> io::Result<MsgResponse<D>>
where
P: AsRef<str>,
D: DeserializeOwned,
{
self.send_msg_body(msg, payload).await?;
self.read_msg().await
}
/// Returns a Future that will send a [Subscribe](event/enum.Subscribe.html)
/// message to i3 along with a list of events to listen to.
pub async fn subscribe<E>(&mut self, events: E) -> io::Result<reply::Success>
where
E: AsRef<[event::Subscribe]>,
{
let sub_json = serde_json::to_string(events.as_ref())?;
self.send_msg_body(msg::Msg::Subscribe, sub_json).await?;
Ok(self.read_msg::<reply::Success>().await?.body)
}
/// Provides a type that implements `Stream` so you can `await` events in a
/// loop
pub fn listen(self) -> FramedRead<UnixStream, codec::EventCodec> {
FramedRead::new(self.stream, codec::EventCodec)
}
/// Run an arbitrary command on i3. Response is a `Vec` of success
/// true/false.
pub async fn run_command<S: AsRef<str>>(
&mut self,
payload: S,
) -> io::Result<Vec<reply::Success>> {
self.send_msg_body(msg::Msg::RunCommand, payload).await?;
Ok(self.read_msg().await?.body)
}
/// Future for getting the current
/// [Workspaces](../reply/struct.Workspace.html), sends
/// [Workspaces](../msg/enum.Msg.html#variant.Workspaces)
pub async fn get_workspaces(&mut self) -> io::Result<reply::Workspaces> {
self.send_msg(msg::Msg::Workspaces).await?;
let resp: MsgResponse<Vec<reply::Workspace>> = self.read_msg().await?;
Ok(resp.body)
}
/// Future that gets all [Outputs](../reply/struct.Outputs.html), sends
/// [Outputs](../msg/enum.Msg.html#variant.Outputs)
pub async fn get_outputs(&mut self) -> io::Result<reply::Outputs> {
self.send_msg(msg::Msg::Outputs).await?;
Ok(self.read_msg().await?.body)
}
/// Future to get complete [Node](../reply/struct.Node.html), sends
/// [Tree](../msg/enum.Msg.html#variant.Tree)
pub async fn get_tree(&mut self) -> io::Result<reply::Node> {
self.send_msg(msg::Msg::Tree).await?;
Ok(self.read_msg().await?.body)
}
/// Get all [Marks](../reply/struct.Marks.html), sends
/// [Marks](../msg/enum.Msg.html#variant.Marks)
pub async fn get_marks(&mut self) -> io::Result<reply::Marks> {
self.send_msg(msg::Msg::Marks).await?;
Ok(self.read_msg().await?.body)
}
/// Future to get all [BarIds](../reply/struct.BarIds.html), sends
/// [BarConfig](../msg/enum.Msg.html#variant.BarConfig)
pub async fn get_bar_ids(&mut self) -> io::Result<reply::BarIds> {
self.send_msg(msg::Msg::BarConfig).await?;
Ok(self.read_msg().await?.body)
}
/// Future to get configs associated with a bar id responds with
/// [BarConfig](../reply/struct.BarConfig.html), sends
/// [BarConfig](../msg/enum.Msg.html#variant.BarConfig)
pub async fn get_bar_config<S: AsRef<str>>(
&mut self,
bar_id: S,
) -> io::Result<reply::BarConfig> {
self.send_msg_body(msg::Msg::BarConfig, bar_id).await?;
Ok(self.read_msg().await?.body)
}
/// Get i3 version
pub async fn get_version(&mut self) -> io::Result<reply::Version> {
self.send_msg(msg::Msg::Version).await?;
Ok(self.read_msg().await?.body)
}
/// Future to get [BindingModes](../reply/struct.BindingModes.html), sends
/// [BindingModes](../msg/enum.Msg.html#variant.BindingModes)
pub async fn get_binding_modes(&mut self) -> io::Result<reply::BindingModes> {
self.send_msg(msg::Msg::BindingModes).await?;
Ok(self.read_msg().await?.body)
}
/// Future for [Config](../reply/struct.Config.html), sends
/// [Config](../msg/enum.Msg.html#variant.Config)
pub async fn get_config(&mut self) -> io::Result<reply::Config> {
self.send_msg(msg::Msg::Config).await?;
Ok(self.read_msg().await?.body)
}
/// Future sends [Tick](../msg/enum.Msg.html#variant.Tick)
pub async fn get_tick(&mut self) -> io::Result<reply::Success> {
self.send_msg(msg::Msg::Tick).await?;
Ok(self.read_msg().await?.body)
}
/// Future [Sync](../msg/enum.Msg.html#variant.Sync)
pub async fn get_sync(&mut self) -> io::Result<reply::Success> {
self.send_msg(msg::Msg::Sync).await?;
Ok(self.read_msg().await?.body)
}
/// Future to get [BindingState](../reply/struct.BindingState.html), sends
/// [BindingState](../msg/enum.Msg.html#variant.BindingModes)
pub async fn get_binding_state(&mut self) -> io::Result<reply::BindingState> {
self.send_msg(msg::Msg::BindingModes).await?;
Ok(self.read_msg().await?.body)
}
}