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
use serde::{de::DeserializeOwned, Serialize};
use std::{env, io, process::Command};
#[cfg(feature = "async-traits")]
use tokio::io::{AsyncRead, AsyncWrite};
pub mod event;
pub mod msg;
pub mod reply;
pub trait Connect {
type Stream: I3IPC;
fn connect() -> io::Result<Self::Stream>;
}
pub const MAGIC: &str = "i3-ipc";
pub trait I3Protocol {
const MAGIC: &'static str = MAGIC;
fn _encode_msg<P>(&self, msg: msg::Msg, payload: Option<P>) -> Vec<u8>
where
P: AsRef<str>,
{
let mut buf = Vec::with_capacity(14);
buf.extend(<Self as I3Protocol>::MAGIC.as_bytes());
if let Some(p) = &payload {
buf.extend(&(p.as_ref().len() as u32).to_ne_bytes());
} else {
buf.extend(&(0_u32).to_ne_bytes());
}
buf.extend(&<u32 as From<msg::Msg>>::from(msg).to_ne_bytes());
if let Some(p) = &payload {
buf.extend(p.as_ref().as_bytes());
}
buf
}
fn encode_msg(&self, msg: msg::Msg) -> Vec<u8> {
self._encode_msg::<&str>(msg, None)
}
fn encode_msg_body<P>(&self, msg: msg::Msg, payload: P) -> Vec<u8>
where
P: AsRef<str>,
{
self._encode_msg(msg, Some(payload))
}
fn encode_msg_json<P>(&self, msg: msg::Msg, payload: P) -> io::Result<Vec<u8>>
where
P: Serialize,
{
Ok(self.encode_msg_body(msg, serde_json::to_string(&payload)?))
}
fn decode_event(evt_type: u32, payload: Vec<u8>) -> io::Result<event::Event> {
decode_event(evt_type, payload)
}
}
pub trait I3IPC: io::Read + io::Write + I3Protocol {
fn decode_msg(&mut self) -> io::Result<(u32, Vec<u8>)> {
let mut buf = [0_u8; 6];
self.read_exact(&mut buf)?;
if &buf[..] != <Self as I3Protocol>::MAGIC.as_bytes() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Expected 'i3-ipc' but received: {:?}", buf),
));
}
let mut intbuf = [0_u8; 4];
self.read_exact(&mut intbuf)?;
let len = u32::from_ne_bytes(intbuf);
let mut msgbuf = [0_u8; 4];
self.read_exact(&mut msgbuf)?;
let msgtype = u32::from_ne_bytes(msgbuf);
let mut payload_buf = vec![0_u8; len as usize];
self.read_exact(&mut payload_buf)?;
Ok((msgtype, payload_buf))
}
}
#[cfg(feature = "async-traits")]
impl<T: AsyncRead + AsyncWrite> I3Protocol for T {}
impl<T: io::Read + io::Write + I3Protocol> I3IPC for T {}
#[cfg(not(feature = "async-traits"))]
impl<T: io::Read + io::Write> I3Protocol for T {}
#[derive(Debug)]
pub struct MsgResponse<D> {
pub msg_type: msg::Msg,
pub body: D,
}
impl<D: DeserializeOwned> MsgResponse<D> {
pub fn new(msg_type: u32, buf: Vec<u8>) -> io::Result<Self> {
Ok(MsgResponse {
msg_type: msg_type.into(),
body: serde_json::from_slice(&buf[..])?,
})
}
}
pub fn socket_path() -> io::Result<String> {
if let Ok(p) = env::var("I3SOCK") {
return Ok(p);
}
let out = Command::new("i3").arg("--get-socketpath").output()?;
if out.status.success() {
Ok(String::from_utf8_lossy(&out.stdout).trim_end().to_string())
} else {
Err(io::Error::new(
io::ErrorKind::BrokenPipe,
"Unable to get i3 socket path",
))
}
}
pub fn decode_event<P>(evt_type: u32, payload: P) -> io::Result<event::Event>
where
P: AsRef<[u8]>,
{
use event::{Event, Subscribe};
let evt_type = evt_type & !(1 << 31);
let body = match evt_type.into() {
Subscribe::Workspace => Event::Workspace(Box::new(serde_json::from_slice::<
event::WorkspaceData,
>(payload.as_ref())?)),
Subscribe::Output => Event::Output(serde_json::from_slice::<event::OutputData>(
payload.as_ref(),
)?),
Subscribe::Mode => {
Event::Mode(serde_json::from_slice::<event::ModeData>(payload.as_ref())?)
}
Subscribe::Window => Event::Window(Box::new(serde_json::from_slice::<event::WindowData>(
payload.as_ref(),
)?)),
Subscribe::BarConfigUpdate => Event::BarConfig(serde_json::from_slice::<
event::BarConfigData,
>(payload.as_ref())?),
Subscribe::Binding => Event::Binding(serde_json::from_slice::<event::BindingData>(
payload.as_ref(),
)?),
Subscribe::Shutdown => Event::Shutdown(serde_json::from_slice::<event::ShutdownData>(
payload.as_ref(),
)?),
Subscribe::Tick => {
Event::Tick(serde_json::from_slice::<event::TickData>(payload.as_ref())?)
}
};
Ok(body)
}