Use swayipc crate instead of home-grown IPC structs

That saves a lot of boring code. :-)
timeout_old
Tassilo Horn 4 years ago
parent 68da44f9f1
commit 32859713d4
  1. 1
      Cargo.toml
  2. 4
      src/bin/swayrd.rs
  3. 30
      src/client.rs
  4. 69
      src/con.rs
  5. 179
      src/demon.rs
  6. 244
      src/ipc.rs

@ -13,3 +13,4 @@ serde = { version = "1.0.117", features = ["derive"] }
serde_json = "1.0.59" serde_json = "1.0.59"
clap = "3.0.0-beta.2" clap = "3.0.0-beta.2"
users = "0.11.0" users = "0.11.0"
swayipc = "2.7.2"

@ -11,12 +11,12 @@ use swayr::demon;
use swayr::ipc; use swayr::ipc;
fn main() { fn main() {
let con_props: Arc<RwLock<HashMap<ipc::Id, ipc::ConProps>>> = let con_props: Arc<RwLock<HashMap<i64, ipc::ExtraProps>>> =
Arc::new(RwLock::new(HashMap::new())); Arc::new(RwLock::new(HashMap::new()));
let con_props_for_ev_handler = con_props.clone(); let con_props_for_ev_handler = con_props.clone();
thread::spawn(move || { thread::spawn(move || {
demon::monitor_con_events(con_props_for_ev_handler); demon::monitor_sway_events(con_props_for_ev_handler);
}); });
demon::serve_client_requests(con_props); demon::serve_client_requests(con_props);

@ -1,11 +1,14 @@
//! Functions and data structures of the swayr client. //! Functions and data structures of the swayr client.
use crate::con; use crate::con;
use crate::ipc;
use crate::util; use crate::util;
use clap::Clap; use clap::Clap;
use std::fmt; use std::fmt;
use swayipc as s;
use swayipc::reply as r;
#[derive(Clap, Debug)] #[derive(Clap, Debug)]
pub enum SwayrCommand { pub enum SwayrCommand {
/// Switch to next urgent window (if any) or to last recently used window. /// Switch to next urgent window (if any) or to last recently used window.
@ -74,16 +77,23 @@ pub fn exec_swayr_cmd(cmd: &SwayrCommand) {
} }
} }
fn focus_window_by_id(id: ipc::Id) { fn focus_window_by_id(id: i64) {
util::swaymsg(&[format!("[con_id={}]", id).as_str(), "focus"]); util::swaymsg(&[format!("[con_id={}]", id).as_str(), "focus"]);
} }
fn quit_window_by_id(id: ipc::Id) { fn quit_window_by_id(id: i64) {
util::swaymsg(&[format!("[con_id={}]", id).as_str(), "kill"]); util::swaymsg(&[format!("[con_id={}]", id).as_str(), "kill"]);
} }
fn get_tree() -> r::Node {
match s::Connection::new() {
Ok(mut con) => con.get_tree().expect("Got no root node"),
Err(err) => panic!(err),
}
}
pub fn switch_to_urgent_or_lru_window() { pub fn switch_to_urgent_or_lru_window() {
let root = con::get_tree(); let root = get_tree();
let windows = con::get_windows(&root, true); let windows = con::get_windows(&root, true);
if let Some(win) = windows if let Some(win) = windows
.iter() .iter()
@ -98,7 +108,7 @@ pub fn switch_to_urgent_or_lru_window() {
} }
pub fn switch_window() { pub fn switch_window() {
let root = con::get_tree(); let root = get_tree();
let windows = con::get_windows(&root, true); let windows = con::get_windows(&root, true);
if let Some(window) = con::select_window("Switch to window", &windows) { if let Some(window) = con::select_window("Switch to window", &windows) {
@ -112,7 +122,7 @@ pub enum Direction {
} }
pub fn focus_next_window_in_direction(dir: Direction) { pub fn focus_next_window_in_direction(dir: Direction) {
let root = con::get_tree(); let root = get_tree();
let windows = con::get_windows(&root, false); let windows = con::get_windows(&root, false);
if windows.len() < 2 { if windows.len() < 2 {
@ -144,7 +154,7 @@ pub fn focus_next_window_in_direction(dir: Direction) {
} }
pub fn switch_workspace() { pub fn switch_workspace() {
let root = con::get_tree(); let root = get_tree();
let workspaces = con::get_workspaces(&root, false); let workspaces = con::get_workspaces(&root, false);
if let Some(workspace) = if let Some(workspace) =
@ -155,7 +165,7 @@ pub fn switch_workspace() {
} }
pub fn switch_workspace_or_window() { pub fn switch_workspace_or_window() {
let root = con::get_tree(); let root = get_tree();
let workspaces = con::get_workspaces(&root, false); let workspaces = con::get_workspaces(&root, false);
let ws_or_wins = con::WsOrWin::from_workspaces(&workspaces); let ws_or_wins = con::WsOrWin::from_workspaces(&workspaces);
if let Some(ws_or_win) = con::select_workspace_or_window( if let Some(ws_or_win) = con::select_workspace_or_window(
@ -172,7 +182,7 @@ pub fn switch_workspace_or_window() {
} }
pub fn quit_window() { pub fn quit_window() {
let root = con::get_tree(); let root = get_tree();
let windows = con::get_windows(&root, true); let windows = con::get_windows(&root, true);
if let Some(window) = con::select_window("Quit window", &windows) { if let Some(window) = con::select_window("Quit window", &windows) {
@ -181,7 +191,7 @@ pub fn quit_window() {
} }
pub fn quit_workspace_or_window() { pub fn quit_workspace_or_window() {
let root = con::get_tree(); let root = get_tree();
let workspaces = con::get_workspaces(&root, false); let workspaces = con::get_workspaces(&root, false);
let ws_or_wins = con::WsOrWin::from_workspaces(&workspaces); let ws_or_wins = con::WsOrWin::from_workspaces(&workspaces);
if let Some(ws_or_win) = if let Some(ws_or_win) =

@ -2,46 +2,22 @@
use crate::ipc; use crate::ipc;
use crate::util; use crate::util;
use ipc::NodeMethods;
use std::cmp; use std::cmp;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::os::unix::net::UnixStream; use std::os::unix::net::UnixStream;
use swayipc::reply as r;
pub fn get_tree() -> ipc::Node {
let output = util::swaymsg(&["-t", "get_tree"]);
let json = output.as_str();
let result = serde_json::from_str(json);
match result {
Ok(node) => node,
Err(err) => {
panic!(
"Can't read get_tree response: {}\nThe JSON is: {}",
err, json
);
}
}
}
#[test]
fn test_get_tree() {
let tree = get_tree();
println!("Those IDs are in get_tree():");
for n in tree.iter() {
println!(" id: {}, type: {:?}", n.id, n.r#type);
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct Window<'a> { pub struct Window<'a> {
node: &'a ipc::Node, node: &'a r::Node,
workspace: &'a ipc::Node, workspace: &'a r::Node,
con_props: Option<ipc::ConProps>, con_props: Option<ipc::ExtraProps>,
} }
impl Window<'_> { impl Window<'_> {
pub fn get_id(&self) -> ipc::Id { pub fn get_id(&self) -> i64 {
self.node.id self.node.id
} }
@ -131,8 +107,8 @@ impl<'a> fmt::Display for Window<'a> {
} }
fn build_windows( fn build_windows(
root: &ipc::Node, root: &r::Node,
mut con_props: HashMap<ipc::Id, ipc::ConProps>, mut con_props: HashMap<i64, ipc::ExtraProps>,
) -> Vec<Window> { ) -> Vec<Window> {
let mut v = vec![]; let mut v = vec![];
for workspace in root.workspaces() { for workspace in root.workspaces() {
@ -148,8 +124,8 @@ fn build_windows(
} }
fn build_workspaces( fn build_workspaces(
root: &ipc::Node, root: &r::Node,
mut con_props: HashMap<ipc::Id, ipc::ConProps>, mut con_props: HashMap<i64, ipc::ExtraProps>,
) -> Vec<Workspace> { ) -> Vec<Workspace> {
let mut v = vec![]; let mut v = vec![];
for workspace in root.workspaces() { for workspace in root.workspaces() {
@ -173,8 +149,7 @@ fn build_workspaces(
v v
} }
fn get_con_props() -> Result<HashMap<ipc::Id, ipc::ConProps>, serde_json::Error> fn get_con_props() -> Result<HashMap<i64, ipc::ExtraProps>, serde_json::Error> {
{
if let Ok(sock) = UnixStream::connect(util::get_swayr_socket_path()) { if let Ok(sock) = UnixStream::connect(util::get_swayr_socket_path()) {
serde_json::from_reader(sock) serde_json::from_reader(sock)
} else { } else {
@ -183,7 +158,7 @@ fn get_con_props() -> Result<HashMap<ipc::Id, ipc::ConProps>, serde_json::Error>
} }
/// Gets all application windows of the tree. /// Gets all application windows of the tree.
pub fn get_windows(root: &ipc::Node, sort: bool) -> Vec<Window> { pub fn get_windows(root: &r::Node, sort: bool) -> Vec<Window> {
let con_props = if sort { let con_props = if sort {
match get_con_props() { match get_con_props() {
Ok(con_props) => Some(con_props), Ok(con_props) => Some(con_props),
@ -205,7 +180,7 @@ pub fn get_windows(root: &ipc::Node, sort: bool) -> Vec<Window> {
/// Gets all application windows of the tree. /// Gets all application windows of the tree.
pub fn get_workspaces( pub fn get_workspaces(
root: &ipc::Node, root: &r::Node,
include_scratchpad: bool, include_scratchpad: bool,
) -> Vec<Workspace> { ) -> Vec<Workspace> {
let con_props = match get_con_props() { let con_props = match get_con_props() {
@ -229,18 +204,6 @@ pub fn get_workspaces(
workspaces workspaces
} }
#[test]
fn test_get_windows() {
let root = get_tree();
let cons = get_windows(&root, true);
println!("There are {} cons.", cons.len());
for c in cons {
println!(" {}", c);
}
}
pub fn select_window<'a>( pub fn select_window<'a>(
prompt: &'a str, prompt: &'a str,
windows: &'a [Window], windows: &'a [Window],
@ -295,8 +258,8 @@ pub fn select_workspace_or_window<'a>(
} }
pub struct Workspace<'a> { pub struct Workspace<'a> {
node: &'a ipc::Node, node: &'a r::Node,
con_props: Option<ipc::ConProps>, con_props: Option<ipc::ExtraProps>,
pub windows: Vec<Window<'a>>, pub windows: Vec<Window<'a>>,
} }
@ -305,7 +268,7 @@ impl Workspace<'_> {
self.node.name.as_ref().unwrap() self.node.name.as_ref().unwrap()
} }
pub fn get_id(&self) -> ipc::Id { pub fn get_id(&self) -> i64 {
self.node.id self.node.id
} }

@ -2,116 +2,123 @@
use crate::ipc; use crate::ipc;
use crate::util; use crate::util;
use serde_json::Deserializer;
use std::collections::HashMap; use std::collections::HashMap;
use std::io::Write; use std::io::Write;
use std::os::unix::net::{UnixListener, UnixStream}; use std::os::unix::net::{UnixListener, UnixStream};
use std::process as proc;
use std::sync::Arc; use std::sync::Arc;
use std::sync::RwLock; use std::sync::RwLock;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
pub fn monitor_con_events( use swayipc as s;
con_props: Arc<RwLock<HashMap<ipc::Id, ipc::ConProps>>>, use swayipc::reply as r;
pub fn monitor_sway_events(
extra_props: Arc<RwLock<HashMap<i64, ipc::ExtraProps>>>,
) { ) {
let mut child = proc::Command::new("swaymsg") let iter = s::Connection::new()
.arg("--monitor") .expect("Could not connect!")
.arg("--raw") .subscribe(&[s::EventType::Window, s::EventType::Workspace])
.arg("-t") .expect("Could not subscribe to window and workspace events.");
.arg("subscribe")
.arg("[\"window\", \"workspace\"]") for ev_result in iter {
.stdout(proc::Stdio::piped()) let handled;
.spawn() match ev_result {
.expect("Failed to subscribe to window events"); Ok(ev) => match ev {
let stdout = child.stdout.take().unwrap(); r::Event::Window(win_ev) => {
let reader = std::io::BufReader::new(stdout); let extra_props_clone = extra_props.clone();
let deserializer = Deserializer::from_reader(reader); handled = handle_window_event(win_ev, extra_props_clone);
for res in deserializer.into_iter::<ipc::ConEvent>() { }
match res { r::Event::Workspace(ws_ev) => {
Ok(win_ev) => handle_con_event(win_ev, con_props.clone()), let extra_props_clone = extra_props.clone();
Err(err) => eprintln!("Error handling window event:\n{:?}", err), handled = handle_workspace_event(ws_ev, extra_props_clone);
}
_ => handled = false,
},
Err(e) => {
eprintln!("Error while receiving events: {}", e);
handled = false;
}
}
if handled {
println!(
"New extra_props state:\n{:#?}",
*extra_props.read().unwrap()
);
} }
} }
}
match child.try_wait() { fn handle_window_event(
Ok(exit_code) => match exit_code { ev: Box<r::WindowEvent>,
None => { extra_props: Arc<RwLock<HashMap<i64, ipc::ExtraProps>>>,
eprintln!("Stopped monitoring con events. Restarting..."); ) -> bool {
monitor_con_events(con_props) let r::WindowEvent { change, container } = *ev;
} match change {
Some(exit_code) => { r::WindowChange::New | r::WindowChange::Focus => {
println!("Swaymsg exited with code {}. Exiting.", exit_code) update_last_focus_time(container.id, extra_props);
} true
}, }
Err(err) => println!("Swaymsg errored with {}. Exiting.", err), r::WindowChange::Close => {
remove_extra_props(container.id, extra_props);
true
}
_ => false,
}
}
fn handle_workspace_event(
ev: Box<r::WorkspaceEvent>,
extra_props: Arc<RwLock<HashMap<i64, ipc::ExtraProps>>>,
) -> bool {
let r::WorkspaceEvent {
change,
current,
old: _,
} = *ev;
match change {
r::WorkspaceChange::Init | r::WorkspaceChange::Focus => {
update_last_focus_time(
current
.expect("No current in Init or Focus workspace event")
.id,
extra_props,
);
true
}
r::WorkspaceChange::Empty => {
remove_extra_props(
current.expect("No current in Empty workspace event").id,
extra_props,
);
false
}
_ => false,
} }
} }
fn update_last_focus_time( fn update_last_focus_time(
id: ipc::Id, id: i64,
con_props: Arc<RwLock<HashMap<ipc::Id, ipc::ConProps>>>, extra_props: Arc<RwLock<HashMap<i64, ipc::ExtraProps>>>,
) { ) {
let mut write_lock = con_props.write().unwrap(); let mut write_lock = extra_props.write().unwrap();
if let Some(mut wp) = write_lock.get_mut(&id) { if let Some(mut wp) = write_lock.get_mut(&id) {
wp.last_focus_time = get_epoch_time_as_millis(); wp.last_focus_time = get_epoch_time_as_millis();
} else { } else {
write_lock.insert( write_lock.insert(
id, id,
ipc::ConProps { ipc::ExtraProps {
last_focus_time: get_epoch_time_as_millis(), last_focus_time: get_epoch_time_as_millis(),
}, },
); );
} }
} }
fn remove_con_props( fn remove_extra_props(
id: ipc::Id, id: i64,
con_props: Arc<RwLock<HashMap<ipc::Id, ipc::ConProps>>>, extra_props: Arc<RwLock<HashMap<i64, ipc::ExtraProps>>>,
) { ) {
con_props.write().unwrap().remove(&id); extra_props.write().unwrap().remove(&id);
}
fn handle_con_event(
ev: ipc::ConEvent,
con_props: Arc<RwLock<HashMap<ipc::Id, ipc::ConProps>>>,
) {
let mut handled = true;
let con_props2 = con_props.clone();
match ev {
ipc::ConEvent::WindowEvent { change, container } => match change {
ipc::WindowEventType::New | ipc::WindowEventType::Focus => {
update_last_focus_time(container.id, con_props)
}
ipc::WindowEventType::Close => {
remove_con_props(container.id, con_props)
}
_ => handled = false,
},
ipc::ConEvent::WorkspaceEvent {
change,
current,
old: _,
} => match change {
ipc::WorkspaceEventType::Init | ipc::WorkspaceEventType::Focus => {
update_last_focus_time(
current
.expect("No current in Init or Focus workspace event")
.id,
con_props,
)
}
ipc::WorkspaceEventType::Empty => remove_con_props(
current.expect("No current in Empty workspace event").id,
con_props,
),
_ => handled = false,
},
}
if handled {
println!("New con_props state:\n{:#?}", *con_props2.read().unwrap());
}
} }
fn get_epoch_time_as_millis() -> u128 { fn get_epoch_time_as_millis() -> u128 {
@ -122,7 +129,7 @@ fn get_epoch_time_as_millis() -> u128 {
} }
pub fn serve_client_requests( pub fn serve_client_requests(
con_props: Arc<RwLock<HashMap<ipc::Id, ipc::ConProps>>>, extra_props: Arc<RwLock<HashMap<i64, ipc::ExtraProps>>>,
) { ) {
match std::fs::remove_file(util::get_swayr_socket_path()) { match std::fs::remove_file(util::get_swayr_socket_path()) {
Ok(()) => println!("Deleted stale socket from previous run."), Ok(()) => println!("Deleted stale socket from previous run."),
@ -134,7 +141,7 @@ pub fn serve_client_requests(
for stream in listener.incoming() { for stream in listener.incoming() {
match stream { match stream {
Ok(stream) => { Ok(stream) => {
handle_client_request(stream, con_props.clone()); handle_client_request(stream, extra_props.clone());
} }
Err(err) => { Err(err) => {
eprintln!("Error handling client request: {}", err); eprintln!("Error handling client request: {}", err);
@ -151,9 +158,9 @@ pub fn serve_client_requests(
fn handle_client_request( fn handle_client_request(
mut stream: UnixStream, mut stream: UnixStream,
con_props: Arc<RwLock<HashMap<ipc::Id, ipc::ConProps>>>, extra_props: Arc<RwLock<HashMap<i64, ipc::ExtraProps>>>,
) { ) {
let json = serde_json::to_string(&*con_props.read().unwrap()).unwrap(); let json = serde_json::to_string(&*extra_props.read().unwrap()).unwrap();
if let Err(err) = stream.write_all(json.as_bytes()) { if let Err(err) = stream.write_all(json.as_bytes()) {
eprintln!("Error writing to client: {:?}", err); eprintln!("Error writing to client: {:?}", err);
} }

@ -1,168 +1,28 @@
//! Functions and data structures concerned with sway JSON IPC. //! Extensions of swayipc types and IPC structs.
extern crate serde; extern crate serde;
extern crate serde_json; extern crate serde_json;
extern crate swayipc;
extern crate users; extern crate users;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use swayipc::reply as r;
pub type Id = u32; /// Immutable Node Iterator
pub type Dim = u16; ///
pub type Pos = i16; // Position can be off-screen, so i16 instead of u16. /// Iterates nodes in depth-first order, tiled nodes before floating nodes.
pub type Pid = u32;
#[derive(Deserialize, Debug)]
#[allow(dead_code)]
pub struct Rect {
pub x: Pos,
pub y: Pos,
pub width: Dim,
pub height: Dim,
}
// TODO: Maybe there are more?
#[derive(Deserialize, Debug)]
pub enum Border {
#[serde(rename = "normal")]
Normal,
#[serde(rename = "none")]
None,
#[serde(rename = "pixel")]
Pixel,
#[serde(rename = "csd")]
Csd,
}
// TODO: Maybe there are more?
#[derive(Deserialize, Debug)]
pub enum Layout {
#[serde(rename = "splith")]
SplitH,
#[serde(rename = "splitv")]
SplitV,
#[serde(rename = "tabbed")]
Tabbed,
#[serde(rename = "stacked")]
Stacked,
#[serde(rename = "output")]
Output,
#[serde(rename = "none")]
None,
}
#[derive(Deserialize, Debug)]
pub enum Orientation {
#[serde(rename = "horizontal")]
Horizontal,
#[serde(rename = "vertical")]
Vertical,
#[serde(rename = "none")]
None,
}
#[derive(Deserialize, PartialEq, Debug)]
pub enum NodeType {
#[serde(rename = "root")]
Root,
#[serde(rename = "workspace")]
Workspace,
#[serde(rename = "output")]
Output,
#[serde(rename = "con")]
Con,
#[serde(rename = "floating_con")]
FloatingCon,
}
#[derive(Deserialize, Debug)]
pub enum ShellType {
#[serde(rename = "xdg_shell")]
XdgShell,
#[serde(rename = "xwayland")]
XWayland,
}
#[derive(Deserialize, Debug)]
#[allow(dead_code)]
pub struct WindowProperties {
pub class: Option<String>,
pub instance: Option<String>,
pub title: Option<String>,
//pub window_type: Option<WindowType>
//pub transient_for: DONTKNOW,
}
#[derive(Deserialize, Debug)]
#[allow(dead_code)]
pub struct Node {
pub id: Id,
pub name: Option<String>,
pub rect: Rect,
pub focused: bool,
pub focus: Vec<Id>,
pub border: Border,
pub current_border_width: Dim,
pub layout: Layout,
pub orientation: Orientation,
pub percent: Option<f64>,
pub window_rect: Rect,
pub deco_rect: Rect,
pub geometry: Rect,
pub window: Option<Id>,
pub urgent: bool,
pub marks: Vec<String>,
pub fullscreen_mode: u8, // TODO: actually, it's 0 or 1, i.e., a bool
pub nodes: Vec<Node>,
pub floating_nodes: Vec<Node>,
pub sticky: bool,
pub r#type: NodeType,
pub app_id: Option<String>,
pub visible: Option<bool>,
pub max_render_time: Option<i32>,
pub pid: Option<Pid>,
pub shell: Option<ShellType>,
pub window_properties: Option<WindowProperties>,
}
impl Node {
pub fn iter(&self) -> NodeIter {
NodeIter::new(self)
}
pub fn windows(&self) -> Vec<&Node> {
self.iter()
.filter(|n| {
(n.r#type == NodeType::Con || n.r#type == NodeType::FloatingCon)
&& n.name.is_some()
})
.collect()
}
pub fn workspaces(&self) -> Vec<&Node> {
self.iter()
.filter(|n| n.r#type == NodeType::Workspace)
.collect()
}
pub fn outputs(&self) -> Vec<&Node> {
self.iter()
.filter(|n| n.r#type == NodeType::Output)
.collect()
}
}
pub struct NodeIter<'a> { pub struct NodeIter<'a> {
stack: Vec<&'a Node>, stack: Vec<&'a r::Node>,
} }
impl<'a> NodeIter<'a> { impl<'a> NodeIter<'a> {
fn new(node: &'a Node) -> NodeIter { pub fn new(node: &'a r::Node) -> NodeIter {
NodeIter { stack: vec![node] } NodeIter { stack: vec![node] }
} }
} }
impl<'a> Iterator for NodeIter<'a> { impl<'a> Iterator for NodeIter<'a> {
type Item = &'a Node; type Item = &'a r::Node;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if let Some(node) = self.stack.pop() { if let Some(node) = self.stack.pop() {
@ -179,67 +39,43 @@ impl<'a> Iterator for NodeIter<'a> {
} }
} }
#[derive(Deserialize, Debug)] /// Extension methods for [`swayipc::reply::Node`].
#[allow(dead_code)] pub trait NodeMethods {
pub enum WindowEventType { /// Returns an iterator for this [`swayipc::reply::Node`] and its childres.
#[serde(rename = "new")] fn iter(&self) -> NodeIter;
New,
#[serde(rename = "close")] /// Returns all nodes being application windows.
Close, fn windows(&self) -> Vec<&r::Node>;
#[serde(rename = "focus")]
Focus,
#[serde(rename = "title")]
Title,
#[serde(rename = "fullscreen_mode")]
FullscreenMode,
#[serde(rename = "move")]
Move,
#[serde(rename = "floating")]
Floating,
#[serde(rename = "urgent")]
Urgent,
#[serde(rename = "mark")]
Mark,
}
#[derive(Deserialize, Debug)] /// Returns all nodes being workspaces.
#[allow(dead_code)] fn workspaces(&self) -> Vec<&r::Node>;
pub enum WorkspaceEventType {
#[serde(rename = "init")]
Init,
#[serde(rename = "empty")]
Empty,
#[serde(rename = "focus")]
Focus,
#[serde(rename = "move")]
Move,
#[serde(rename = "rename")]
Rename,
#[serde(rename = "urgent")]
Urgent,
#[serde(rename = "reload")]
Reload,
} }
#[derive(Deserialize, Debug)] impl NodeMethods for r::Node {
#[serde(untagged)] fn iter(&self) -> NodeIter {
#[allow(dead_code)] NodeIter::new(self)
pub enum ConEvent { }
WindowEvent {
change: WindowEventType, fn windows(&self) -> Vec<&r::Node> {
container: Box<Node>, self.iter()
}, .filter(|n| {
WorkspaceEvent { (n.node_type == r::NodeType::Con
change: WorkspaceEventType, || n.node_type == r::NodeType::FloatingCon)
// On a reload event, current and old are both null. && n.name.is_some()
current: Option<Box<Node>>, })
// If the old was empty, it'll be killed => "old": null. .collect()
old: Option<Box<Node>>, }
},
fn workspaces(&self) -> Vec<&r::Node> {
self.iter()
.filter(|n| n.node_type == r::NodeType::Workspace)
.collect()
}
} }
/// Extra properties gathered by swayrd for windows and workspaces.
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct ConProps { pub struct ExtraProps {
/// Milliseconds since UNIX epoch. /// Milliseconds since UNIX epoch.
pub last_focus_time: u128, pub last_focus_time: u128,
} }

Loading…
Cancel
Save