swayipc-3.0.3/.cargo_vcs_info.json0000644000000001460000000000100125170ustar { "git": { "sha1": "5f182a811d1c9993468fcda1a1e59e804f8f9f95" }, "path_in_vcs": "blocking" }swayipc-3.0.3/Cargo.toml0000644000000021220000000000100105110ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "swayipc" version = "3.0.3" authors = ["Jayce Fayne "] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "A library for controlling sway through its IPC interface" readme = "README.md" keywords = [ "sway", "swaywm", "swayipc", "ipc", ] categories = ["network-programming"] license = "MIT" repository = "https://github.com/jaycefayne/swayipc-rs" [lib] name = "swayipc" path = "src/lib.rs" [dependencies.serde] version = "1" [dependencies.serde_json] version = "1" [dependencies.swayipc-types] version = "1" swayipc-3.0.3/Cargo.toml.orig000064400000000000000000000007171046102023000142020ustar 00000000000000[package] name = "swayipc" version = "3.0.3" authors = ["Jayce Fayne "] edition = "2021" description = "A library for controlling sway through its IPC interface" license = "MIT" repository = "https://github.com/jaycefayne/swayipc-rs" categories = ["network-programming"] keywords = ["sway", "swaywm", "swayipc", "ipc"] readme = "README.md" [dependencies] swayipc-types = { version = "1", path = "../types" } serde = "1" serde_json = "1" swayipc-3.0.3/README.md000064400000000000000000000022671046102023000125740ustar 00000000000000# swayipc   [![Action Badge]][actions] [![Version Badge]][crates.io] [![License Badge]][license] [![Docs Badge]][docs] [Version Badge]: https://img.shields.io/crates/v/swayipc.svg [crates.io]: https://crates.io/crates/swayipc [Action Badge]: https://github.com/JayceFayne/swayipc-rs/workflows/Rust/badge.svg [actions]: https://github.com/JayceFayne/swayipc-rs/actions [License Badge]: https://img.shields.io/crates/l/swayipc.svg [license]: https://github.com/JayceFayne/swayipc-rs/blob/master/LICENSE.md [Docs Badge]: https://docs.rs/swayipc/badge.svg [docs]: https://docs.rs/swayipc A Rust library for controlling swaywm through its [IPC interface](https://github.com/swaywm/sway/blob/master/sway/sway-ipc.7.scd). ## Usage Examples of how to use the library can be found [here](../examples). ## i3 compatibility [i3](https://github.com/i3/i3) compatibility is kept if possible even though this library primarily targets sway. ## Versioning This library targets the latest stable release of [sway](https://github.com/swaywm/sway). ## Contributing If you find any errors in swayipc or just want to add a new feature feel free to [submit a PR](https://github.com/jaycefayne/swayipc-rs/pulls). swayipc-3.0.3/src/common.rs000064400000000000000000000015061046102023000137350ustar 00000000000000use std::io::Read; use std::os::unix::net::UnixStream; use swayipc_types::{Error::InvalidMagic, Fallible, MAGIC}; pub(super) fn receive_from_stream(stream: &mut UnixStream) -> Fallible<(u32, Vec)> { let mut header_buf = [0_u8; 14]; stream.read_exact(&mut header_buf)?; let magic_data: [u8; 6] = header_buf[..6].try_into().unwrap(); if magic_data != MAGIC { return Err(InvalidMagic(magic_data)); } let payload_len_buf: [u8; 4] = header_buf[6..10].try_into().unwrap(); let payload_len = u32::from_ne_bytes(payload_len_buf); let reply_type_buf: [u8; 4] = header_buf[10..14].try_into().unwrap(); let reply_type = u32::from_ne_bytes(reply_type_buf); let mut reply_payload = vec![0_u8; payload_len as usize]; stream.read_exact(&mut reply_payload)?; Ok((reply_type, reply_payload)) } swayipc-3.0.3/src/connection.rs000064400000000000000000000104751046102023000146110ustar 00000000000000use super::common::receive_from_stream; use super::socket::get_socketpath; use crate::{CommandType::*, Error::SubscriptionFailed, *}; use serde::de::DeserializeOwned as Deserialize; use std::io::Write; use std::os::unix::net::UnixStream; #[derive(Debug)] pub struct Connection(UnixStream); impl Connection { /// Creates a new `Connection` to sway-ipc. pub fn new() -> Fallible { let socketpath = get_socketpath()?; let unix_stream = UnixStream::connect(socketpath)?; Ok(Self(unix_stream)) } fn raw_command(&mut self, command_type: CommandType) -> Fallible { self.0.write_all(command_type.encode().as_slice())?; command_type.decode(receive_from_stream(&mut self.0)?) } fn raw_command_with>( &mut self, command_type: CommandType, payload: T, ) -> Fallible { self.0 .write_all(command_type.encode_with(payload).as_slice())?; command_type.decode(receive_from_stream(&mut self.0)?) } /// Runs the payload as sway commands. pub fn run_command>(&mut self, payload: T) -> Fallible>> { let outcome: Vec = self.raw_command_with(RunCommand, payload.as_ref())?; Ok(outcome.into_iter().map(CommandOutcome::decode).collect()) } /// Get the list of current workspaces. pub fn get_workspaces(&mut self) -> Fallible> { self.raw_command(GetWorkspaces) } /// Subscribe the IPC connection to the events listed in the payload. pub fn subscribe>(mut self, events: T) -> Fallible { let events = serde_json::ser::to_string(events.as_ref())?; let res: Success = self.raw_command_with(Subscribe, events.as_bytes())?; if !res.success { return Err(SubscriptionFailed(events)); } Ok(EventStream::new(self.0)) } /// Get the list of current outputs. pub fn get_outputs(&mut self) -> Fallible> { self.raw_command(GetOutputs) } /// Get the node layout tree. pub fn get_tree(&mut self) -> Fallible { self.raw_command(GetTree) } /// Get the names of all the marks currently set. pub fn get_marks(&mut self) -> Fallible> { self.raw_command(GetMarks) } /// Get a list of bar config names. pub fn get_bar_ids(&mut self) -> Fallible> { self.raw_command(GetBarConfig) } /// Get the specified bar config. pub fn get_bar_config>(&mut self, id: T) -> Fallible { self.raw_command_with(GetBarConfig, id.as_ref()) } /// Get the version of sway that owns the IPC socket. pub fn get_version(&mut self) -> Fallible { self.raw_command(GetVersion) } /// Get the list of binding mode names. pub fn get_binding_modes(&mut self) -> Fallible> { self.raw_command(GetBindingModes) } /// Returns the config that was last loaded. pub fn get_config(&mut self) -> Fallible { self.raw_command(GetConfig) } /// Sends a tick event with the specified payload. pub fn send_tick>(&mut self, payload: T) -> Fallible { let res: Success = self.raw_command_with(SendTick, payload.as_ref())?; Ok(res.success) } /// Replies failure object for i3 compatibility. pub fn sync(&mut self) -> Fallible { let res: Success = self.raw_command::(Sync)?; Ok(res.success) } /// Request the current binding state, e.g. the currently active binding /// mode name. pub fn get_binding_state(&mut self) -> Fallible { let state: BindingState = self.raw_command(GetBindingState)?; Ok(state.name) } /// Get the list of input devices. pub fn get_inputs(&mut self) -> Fallible> { self.raw_command(GetInputs) } /// Get the list of seats. pub fn get_seats(&mut self) -> Fallible> { self.raw_command(GetSeats) } } impl From for Connection { fn from(unix_stream: UnixStream) -> Self { Self(unix_stream) } } impl From for UnixStream { fn from(connection: Connection) -> Self { connection.0 } } swayipc-3.0.3/src/event.rs000064400000000000000000000006651046102023000135730ustar 00000000000000use super::common::receive_from_stream; use crate::{Event, Fallible}; use std::os::unix::net::UnixStream; pub struct EventStream(UnixStream); impl EventStream { pub(super) fn new(stream: UnixStream) -> Self { Self(stream) } } impl Iterator for EventStream { type Item = Fallible; fn next(&mut self) -> Option { Some(receive_from_stream(&mut self.0).and_then(Event::decode)) } } swayipc-3.0.3/src/lib.rs000064400000000000000000000003241046102023000132100ustar 00000000000000#![deny(unsafe_code)] #![deny(rust_2018_idioms)] mod common; mod connection; mod event; mod socket; #[cfg(test)] mod tests; pub use connection::Connection; pub use event::EventStream; pub use swayipc_types::*; swayipc-3.0.3/src/socket.rs000064400000000000000000000013411046102023000137320ustar 00000000000000use std::env; use std::io::Read; use std::path::PathBuf; use std::process::{Command, Stdio}; use swayipc_types::{Error, Fallible}; pub fn get_socketpath() -> Fallible { env::var("I3SOCK") .or_else(|_| env::var("SWAYSOCK")) .or_else(|_| spawn("i3")) .or_else(|_| spawn("sway")) .map_err(|_| Error::SocketNotFound) .map(PathBuf::from) } fn spawn(wm: &str) -> Fallible { let mut child = Command::new(wm) .arg("--get-socketpath") .stdout(Stdio::piped()) .spawn()?; let mut buf = String::new(); if let Some(mut stdout) = child.stdout.take() { stdout.read_to_string(&mut buf)?; buf.pop(); } child.wait()?; Ok(buf) } swayipc-3.0.3/src/tests.rs000064400000000000000000000077761046102023000136260ustar 00000000000000use crate::{Connection, EventType}; #[test] fn connect() { Connection::new().unwrap(); } #[test] fn run_command_nothing() { let mut connection = Connection::new().unwrap(); let result = connection.run_command("").unwrap(); assert!(result.is_empty()); } #[test] fn run_command_single_success() { let mut connection = Connection::new().unwrap(); let result = connection.run_command("exec /bin/true").unwrap(); assert_eq!(result.len(), 1); result[0].as_ref().unwrap(); } #[test] fn run_command_multiple_success() { let mut connection = Connection::new().unwrap(); let result = connection .run_command("exec /bin/true; exec /bin/true") .unwrap(); assert_eq!(result.len(), 2); result[0].as_ref().unwrap(); result[1].as_ref().unwrap(); } #[test] fn run_command_fail() { let mut connection = Connection::new().unwrap(); let result = connection.run_command("somerandomcommand").unwrap(); assert_eq!(result.len(), 1); assert!(result[0].as_ref().is_err()); } #[test] fn get_workspaces() { Connection::new().unwrap().get_workspaces().unwrap(); } #[test] fn get_outputs() { Connection::new().unwrap().get_outputs().unwrap(); } #[test] fn get_tree() { Connection::new().unwrap().get_tree().unwrap(); } #[test] fn get_marks() { Connection::new().unwrap().get_marks().unwrap(); } #[test] fn get_bar_ids() { Connection::new().unwrap().get_bar_ids().unwrap(); } #[test] fn get_bar_ids_and_one_config() { let mut connection = Connection::new().unwrap(); let ids = connection.get_bar_ids().unwrap(); connection.get_bar_config(&ids[0]).unwrap(); } #[test] fn get_version() { Connection::new().unwrap().get_version().unwrap(); } #[test] fn get_binding_modes() { Connection::new().unwrap().get_binding_modes().unwrap(); } #[test] fn get_config() { Connection::new().unwrap().get_config().unwrap(); } #[test] fn send_tick() { let success = Connection::new().unwrap().send_tick("").unwrap(); assert!(success); } #[test] fn sync() { let success = Connection::new().unwrap().sync().unwrap(); assert!(!success, "sync should always return false on sway"); } #[test] fn get_binding_state() { Connection::new().unwrap().get_binding_state().unwrap(); } #[test] fn get_inputs() { Connection::new().unwrap().get_inputs().unwrap(); } #[test] fn get_seats() { Connection::new().unwrap().get_seats().unwrap(); } #[test] fn event_subscribe_all() { Connection::new() .unwrap() .subscribe(&[ EventType::Workspace, EventType::Mode, EventType::Window, EventType::BarConfigUpdate, EventType::Binding, EventType::Shutdown, EventType::Tick, EventType::BarStateUpdate, EventType::Input, ]) .unwrap(); } #[test] fn find_in_tree() { assert!(Connection::new() .unwrap() .get_tree() .unwrap() .find_as_ref(|n| n.focused) .is_some()); } #[test] fn find_in_tree_comp() { assert_eq!( Connection::new() .unwrap() .get_tree() .unwrap() .find_as_ref(|n| n.focused), Connection::new() .unwrap() .get_tree() .unwrap() .find(|n| n.focused) .as_ref() ); } #[test] fn find_focused_as_ref() { assert!(Connection::new() .unwrap() .get_tree() .unwrap() .find_focused_as_ref(|n| n.focused) .is_some()); } #[test] fn find_focused() { assert!(Connection::new() .unwrap() .get_tree() .unwrap() .find_focused(|n| n.focused) .is_some()); } #[test] fn find_in_tree_comp_find_focused() { assert_eq!( Connection::new() .unwrap() .get_tree() .unwrap() .find_focused(|n| n.focused), Connection::new() .unwrap() .get_tree() .unwrap() .find(|n| n.focused) ); }