1use crate::models::{
2 InputDevice, Layout, MsgTemplate, OptionValueResponse, Output, View, ViewAlpha,
3 WayfireConfiguration, WorkspaceSet,
4};
5use serde_json::Value;
6use std::env;
7use std::error::Error;
8use std::io;
9use tokio::io::{AsyncReadExt, AsyncWriteExt};
10use tokio::net::UnixStream as TokioUnixStream;
11
12pub struct WayfireSocket {
13 client: TokioUnixStream,
14}
15
16impl WayfireSocket {
17 pub async fn connect() -> io::Result<Self> {
18 let socket_name =
19 env::var("WAYFIRE_SOCKET").expect("WAYFIRE_SOCKET environment variable not set");
20 let client = TokioUnixStream::connect(&socket_name).await?;
21 Ok(WayfireSocket { client })
22 }
23
24 pub async fn send_json(&mut self, msg: &MsgTemplate) -> io::Result<Value> {
25 let data = serde_json::to_vec(msg)?;
26 let header = (data.len() as u32).to_le_bytes();
27
28 self.client.write_all(&header).await?;
29 self.client.write_all(&data).await?;
30
31 self.read_message().await
32 }
33
34 pub async fn read_exact(&mut self, n: usize) -> io::Result<Vec<u8>> {
35 let mut buf = vec![0; n];
36 self.client.read_exact(&mut buf).await?;
37 Ok(buf)
38 }
39
40 pub async fn read_message(&mut self) -> io::Result<Value> {
41 let len_buf = self.read_exact(4).await?;
42 let len = u32::from_le_bytes(len_buf.try_into().unwrap()) as usize;
43
44 let response_buf = self.read_exact(len).await?;
45 let response: Value = serde_json::from_slice(&response_buf)?;
46
47 if response.get("error").is_some() {
48 eprintln!("Error: {:?}", response);
49 }
50
51 Ok(response)
52 }
53
54 pub async fn list_views(&mut self) -> io::Result<Vec<View>> {
55 let message = MsgTemplate {
56 method: "window-rules/list-views".to_string(),
57 data: None,
58 };
59
60 let response = self.send_json(&message).await?;
61 let views: Vec<View> = serde_json::from_value(response)?;
62
63 Ok(views)
64 }
65
66 pub async fn list_outputs(&mut self) -> io::Result<Vec<Output>> {
67 let message = MsgTemplate {
68 method: "window-rules/list-outputs".to_string(),
69 data: None,
70 };
71
72 let response = self.send_json(&message).await?;
73 let outputs: Vec<Output> = serde_json::from_value(response)?;
74
75 Ok(outputs)
76 }
77
78 pub async fn list_wsets(&mut self) -> io::Result<Vec<WorkspaceSet>> {
79 let message = MsgTemplate {
80 method: "window-rules/list-wsets".to_string(),
81 data: None,
82 };
83
84 let response = self.send_json(&message).await?;
85 let workspace_sets: Vec<WorkspaceSet> = serde_json::from_value(response)?;
86
87 Ok(workspace_sets)
88 }
89
90 pub async fn list_input_devices(&mut self) -> io::Result<Vec<InputDevice>> {
91 let message = MsgTemplate {
92 method: "input/list-devices".to_string(),
93 data: None,
94 };
95
96 let response = self.send_json(&message).await?;
97 let input_devices: Vec<InputDevice> = serde_json::from_value(response)?;
98
99 Ok(input_devices)
100 }
101
102 pub async fn get_configuration(&mut self) -> io::Result<WayfireConfiguration> {
103 let message = MsgTemplate {
104 method: "wayfire/configuration".to_string(),
105 data: None,
106 };
107
108 let response = self.send_json(&message).await?;
109 let configuration: WayfireConfiguration = serde_json::from_value(response)?;
110
111 Ok(configuration)
112 }
113
114 pub async fn get_option_value(&mut self, option: &str) -> io::Result<OptionValueResponse> {
115 let message = MsgTemplate {
116 method: "wayfire/get-config-option".to_string(),
117 data: Some(serde_json::json!({
118 "option": option
119 })),
120 };
121
122 let response = self.send_json(&message).await?;
123 let option_value_response: OptionValueResponse = serde_json::from_value(response)?;
124
125 Ok(option_value_response)
126 }
127
128 pub async fn get_output(&mut self, output_id: i64) -> io::Result<Output> {
129 let message = MsgTemplate {
130 method: "window-rules/output-info".to_string(),
131 data: Some(serde_json::json!({
132 "id": output_id
133 })),
134 };
135
136 let response = self.send_json(&message).await?;
137 let output: Output = serde_json::from_value(response)?;
138
139 Ok(output)
140 }
141
142 pub async fn get_view(&mut self, view_id: i64) -> io::Result<View> {
143 let message = MsgTemplate {
144 method: "window-rules/view-info".to_string(),
145 data: Some(serde_json::json!({
146 "id": view_id
147 })),
148 };
149
150 let response = self.send_json(&message).await?;
151
152 let info = response.get("info").ok_or_else(|| {
153 io::Error::new(io::ErrorKind::NotFound, "Missing 'info' field in response")
154 })?;
155
156 let view: View = serde_json::from_value(info.clone())?;
157
158 Ok(view)
159 }
160
161 pub async fn get_focused_view(&mut self) -> Result<View, Box<dyn Error>> {
162 let message = MsgTemplate {
163 method: "window-rules/get-focused-view".to_string(),
164 data: None,
165 };
166
167 let response = self.send_json(&message).await?;
168
169 let view_info = response.get("info").ok_or_else(|| {
170 io::Error::new(io::ErrorKind::NotFound, "Missing 'info' field in response")
171 })?;
172
173 let view: View = serde_json::from_value(view_info.clone())?;
174
175 Ok(view)
176 }
177 pub async fn get_focused_output(&mut self) -> Result<Output, Box<dyn Error>> {
178 let message = MsgTemplate {
179 method: "window-rules/get-focused-output".to_string(),
180 data: None,
181 };
182
183 let response = self.send_json(&message).await?;
184
185 let output_info = response.get("info").ok_or_else(|| {
186 io::Error::new(io::ErrorKind::NotFound, "Missing 'info' field in response")
187 })?;
188
189 let output: Output = serde_json::from_value(output_info.clone())?;
190
191 Ok(output)
192 }
193
194 pub async fn get_view_alpha(&mut self, view_id: i64) -> io::Result<ViewAlpha> {
195 let message = MsgTemplate {
196 method: "wf/alpha/get-view-alpha".to_string(),
197 data: Some(serde_json::json!({
198 "view-id": view_id
199 })),
200 };
201
202 let response = self.send_json(&message).await?;
203
204 let view_alpha: ViewAlpha = serde_json::from_value(response).map_err(|e| {
205 io::Error::new(
206 io::ErrorKind::InvalidData,
207 format!("Failed to parse response: {}", e),
208 )
209 })?;
210
211 Ok(view_alpha)
212 }
213
214 pub async fn set_view_alpha(&mut self, view_id: i64, alpha: f64) -> io::Result<Value> {
215 let message = MsgTemplate {
216 method: "wf/alpha/set-view-alpha".to_string(),
217 data: Some(serde_json::json!({
218 "view-id": view_id,
219 "alpha": alpha
220 })),
221 };
222
223 self.send_json(&message).await
224 }
225
226 pub async fn get_tiling_layout(&mut self, wset: i64, x: i64, y: i64) -> io::Result<Layout> {
227 let message = MsgTemplate {
228 method: "simple-tile/get-layout".to_string(),
229 data: Some(serde_json::json!({
230 "wset-index": wset,
231 "workspace": {
232 "x": x,
233 "y": y
234 }
235 })),
236 };
237
238 let response = self.send_json(&message).await?;
239
240 let layout_value = response.get("layout").ok_or_else(|| {
241 io::Error::new(
242 io::ErrorKind::InvalidData,
243 "Missing `layout` field in response",
244 )
245 })?;
246
247 let layout: Layout = serde_json::from_value(layout_value.clone())?;
248
249 Ok(layout)
250 }
251
252 pub async fn set_tiling_layout(
253 &mut self,
254 wset: i64,
255 x: i64,
256 y: i64,
257 layout: &Layout,
258 ) -> io::Result<Value> {
259 let message = MsgTemplate {
260 method: "simple-tile/set-layout".to_string(),
261 data: Some(serde_json::json!({
262 "wset-index": wset,
263 "workspace": {
264 "x": x,
265 "y": y
266 },
267 "layout": layout
268 })),
269 };
270
271 self.send_json(&message).await
272 }
273
274 pub async fn set_view_fullscreen(&mut self, view_id: i64, state: bool) -> io::Result<Value> {
275 let message = MsgTemplate {
276 method: "wm-actions/set-fullscreen".to_string(),
277 data: Some(serde_json::json!({
278 "view_id": view_id,
279 "state": state
280 })),
281 };
282
283 self.send_json(&message).await
284 }
285
286 pub async fn expo_toggle(&mut self) -> io::Result<Value> {
287 let message = MsgTemplate {
288 method: "expo/toggle".to_string(),
289 data: None,
290 };
291
292 self.send_json(&message).await
293 }
294
295 pub async fn scale_toggle(&mut self) -> io::Result<Value> {
296 let message = MsgTemplate {
297 method: "scale/toggle".to_string(),
298 data: None,
299 };
300
301 self.send_json(&message).await
302 }
303 pub async fn scale_toggle_all(&mut self) -> io::Result<Value> {
304 let message = MsgTemplate {
305 method: "expo/toggle_all".to_string(),
306 data: None,
307 };
308
309 self.send_json(&message).await
310 }
311
312 pub async fn cube_activate(&mut self) -> io::Result<Value> {
313 let message = MsgTemplate {
314 method: "cube/activate".to_string(),
315 data: None,
316 };
317
318 self.send_json(&message).await
319 }
320
321 pub async fn cube_rotate_left(&mut self) -> io::Result<Value> {
322 let message = MsgTemplate {
323 method: "cube/rotate_left".to_string(),
324 data: None,
325 };
326
327 self.send_json(&message).await
328 }
329
330 pub async fn cube_rotate_right(&mut self) -> io::Result<Value> {
331 let message = MsgTemplate {
332 method: "cube/rotate_right".to_string(),
333 data: None,
334 };
335
336 self.send_json(&message).await
337 }
338
339 pub async fn toggle_showdesktop(&mut self) -> io::Result<Value> {
340 let message = MsgTemplate {
341 method: "wm-actions/toggle_showdesktop".to_string(),
342 data: None,
343 };
344 self.send_json(&message).await
345 }
346
347 pub async fn set_view_sticky(&mut self, view_id: i64, state: bool) -> io::Result<Value> {
348 let message = MsgTemplate {
349 method: "wm-actions/set-sticky".to_string(),
350 data: Some(serde_json::json!({
351 "view_id": view_id,
352 "state": state,
353 })),
354 };
355 self.send_json(&message).await
356 }
357
358 pub async fn send_view_to_back(&mut self, view_id: i64, state: bool) -> io::Result<Value> {
359 let message = MsgTemplate {
360 method: "wm-actions/send-to-back".to_string(),
361 data: Some(serde_json::json!({
362 "view_id": view_id,
363 "state": state,
364 })),
365 };
366 self.send_json(&message).await
367 }
368
369 pub async fn set_view_minimized(&mut self, view_id: i64, state: bool) -> io::Result<Value> {
370 let message = MsgTemplate {
371 method: "wm-actions/set-minimized".to_string(),
372 data: Some(serde_json::json!({
373 "view_id": view_id,
374 "state": state,
375 })),
376 };
377 self.send_json(&message).await
378 }
379
380 pub async fn configure_input_device(&mut self, id: i64, enabled: bool) -> io::Result<Value> {
381 let message = MsgTemplate {
382 method: "input/configure-device".to_string(),
383 data: Some(serde_json::json!({
384 "id": id,
385 "enabled": enabled,
386 })),
387 };
388 self.send_json(&message).await
389 }
390
391 pub async fn close_view(&mut self, view_id: i64) -> io::Result<Value> {
392 let message = MsgTemplate {
393 method: "window-rules/close-view".to_string(),
394 data: Some(serde_json::json!({
395 "id": view_id,
396 })),
397 };
398 self.send_json(&message).await
399 }
400
401 pub async fn wset_info(&mut self, id: i64) -> io::Result<serde_json::Value> {
402 let message = MsgTemplate {
403 method: "window-rules/wset-info".to_string(),
404 data: Some(serde_json::json!({
405 "id": id,
406 })),
407 };
408
409 self.send_json(&message).await
410 }
411
412 pub async fn watch(&mut self, events: Option<Vec<String>>) -> io::Result<serde_json::Value> {
413 let mut data = serde_json::json!({});
414 if let Some(events) = events {
415 data["events"] = serde_json::json!(events);
416 }
417
418 let message = MsgTemplate {
419 method: "window-rules/events/watch".to_string(),
420 data: Some(data),
421 };
422
423 self.send_json(&message).await
424 }
425
426 pub async fn configure_view(
427 &mut self,
428 view_id: i64,
429 x: i64,
430 y: i64,
431 w: i64,
432 h: i64,
433 output_id: Option<i64>,
434 ) -> io::Result<serde_json::Value> {
435 let mut data = serde_json::json!({
436 "id": view_id,
437 "geometry": {
438 "x": x,
439 "y": y,
440 "width": w,
441 "height": h
442 }
443 });
444
445 if let Some(output_id) = output_id {
446 data["output_id"] = serde_json::json!(output_id);
447 }
448
449 let message = MsgTemplate {
450 method: "window-rules/configure-view".to_string(),
451 data: Some(data),
452 };
453
454 self.send_json(&message).await
455 }
456
457 pub async fn assign_slot(&mut self, view_id: i64, slot: &str) -> io::Result<serde_json::Value> {
458 let message = MsgTemplate {
459 method: format!("grid/{}", slot),
460 data: Some(serde_json::json!({
461 "view_id": view_id
462 })),
463 };
464
465 self.send_json(&message).await
466 }
467
468 pub async fn set_focus(&mut self, view_id: i64) -> io::Result<serde_json::Value> {
469 let message = MsgTemplate {
470 method: "window-rules/focus-view".to_string(),
471 data: Some(serde_json::json!({
472 "id": view_id
473 })),
474 };
475
476 self.send_json(&message).await
477 }
478
479 pub async fn set_workspace(
480 &mut self,
481 x: i64,
482 y: i64,
483 view_id: i64,
484 output_id: i64,
485 ) -> io::Result<Value> {
486 let message = MsgTemplate {
487 method: "vswitch/set-workspace".to_string(),
488 data: Some(serde_json::json!({
489 "x": x,
490 "y": y,
491 "output-id": output_id,
492 "view-id": view_id
493 })),
494 };
495
496 self.send_json(&message).await
497 }
498
499 pub async fn create_headless_output(&mut self, width: u32, height: u32) -> io::Result<Value> {
500 let message = MsgTemplate {
501 method: "wayfire/create-headless-output".to_string(),
502 data: Some(serde_json::json!({
503 "width": width,
504 "height": height
505 })),
506 };
507
508 self.send_json(&message).await
509 }
510
511 pub async fn destroy_headless_output(
512 &mut self,
513 output_name: Option<String>,
514 output_id: Option<i64>,
515 ) -> io::Result<Value> {
516 let mut data = serde_json::json!({});
517
518 if let Some(name) = output_name {
519 data["output"] = serde_json::Value::String(name);
520 } else if let Some(id) = output_id {
521 data["output-id"] = serde_json::Value::Number(serde_json::Number::from(id));
522 } else {
523 return Err(io::Error::new(
524 io::ErrorKind::InvalidInput,
525 "Either output_name or output_id must be provided",
526 ));
527 }
528
529 let message = MsgTemplate {
530 method: "wayfire/destroy-headless-output".to_string(),
531 data: Some(data),
532 };
533
534 self.send_json(&message).await
535 }
536}