#i3 #tokio #ipc #async #protocols #window-event #api-bindings

tokio-i3ipc

Bindings for i3 and tokio allowing async applications to communicate with i3 over it's IPC interface. Contains futures implementations and convenience functions for working with i3.

11 breaking releases

0.16.0 Dec 31, 2022
0.15.0 Jul 5, 2022
0.14.0 Dec 10, 2021
0.13.0 Nov 4, 2021
0.5.0 Apr 21, 2019

#303 in Asynchronous

Download history 1/week @ 2023-12-04 7/week @ 2023-12-11 7/week @ 2023-12-18 4/week @ 2023-12-25 12/week @ 2024-01-15 7/week @ 2024-01-22 4/week @ 2024-02-05 23/week @ 2024-02-12 13/week @ 2024-02-19 91/week @ 2024-02-26 23/week @ 2024-03-04 25/week @ 2024-03-11 35/week @ 2024-03-18

175 downloads per month
Used in 5 crates

MIT license

49KB
935 lines

tokio-i3ipc

Now with tokio 1.0 support! Use version 0.12.0 and up for tokio 1.0

Build Status Crate API

This crate provides types and functions for working with i3's IPC protocol within tokio. It re-exports the crate i3ipc-types because it is also used for a synchronous version of the code.

I expect the most common use case will be to subscribe to some events and listen:

use std::io;
use tokio::stream::StreamExt;
use tokio_i3ipc::{
    event::{Event, Subscribe},
    I3,
};

#[tokio::main(flavor = "current_thread")]
async fn main() -> io::Result<()> {
    let mut i3 = I3::connect().await?;
    let resp = i3.subscribe([Subscribe::Window]).await?;

    println!("{:#?}", resp);
    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(())
}

Another example, getting all workspaces from i3:

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(())
}

or, you could write any get_* yourself using the same methods it does:

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(())
}

send_msg, will handle writing to i3. read_msg and will handle reading.

Sending Messages to i3

To send messages to i3, there are a number of convenience methods on I3.

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 outputs = i3.get_outputs().await?;
    println!("{:#?}", worksp);

    Ok(())
}

"Real world" example

I have a fork of an i3 window-logging application that uses tokio-i3ipc (https://github.com/leshow/i3-tracker-rs/). The tracker subscribes to window events and logs how much time is spent on each node.

Dependencies

~4–15MB
~157K SLoC