use breadx::{
auto::xproto::ExposeEvent, rgb, AsyncDisplayConnection, Event, EventMask, GcParameters, Rectangle,
};
use easy_parallel::Parallel;
use futures_lite::{future, FutureExt, StreamExt};
use smol::{
channel::{unbounded, Receiver, Sender},
Executor, Timer,
};
use std::{mem, time::Duration};
#[inline]
async fn rng(sender: Sender<[u8; 3]>) {
let mut timer = Timer::interval(Duration::from_secs(3));
loop {
let random: [u8; 3] = [fastrand::u8(..), fastrand::u8(..), fastrand::u8(..)];
let res = sender.send(random).await;
if res.is_err() {
break;
}
timer.next().await.unwrap();
}
}
#[inline]
async fn x_process(receiver: Receiver<[u8; 3]>) -> breadx::Result<()> {
enum MainLoopDirective {
ProcessingEvent(breadx::Result<Event>),
ProcessingChannel([u8; 3]),
}
let mut width: u16 = 600;
let mut height: u16 = 480;
let mut conn = AsyncDisplayConnection::create_async(None, None).await?;
let win = conn
.create_simple_window_async(
conn.default_screen().root,
0,
0,
width,
height,
0,
conn.default_black_pixel(),
conn.default_white_pixel(),
)
.await?;
win.set_event_mask_async(&mut conn, EventMask::EXPOSURE | EventMask::STRUCTURE_NOTIFY)
.await?;
win.map_async(&mut conn).await?;
win.set_title_async(&mut conn, "Async Example").await?;
let gc = conn
.create_gc_async(
win,
GcParameters {
foreground: Some(conn.default_black_pixel()),
graphics_exposures: Some(0),
..Default::default()
},
)
.await?;
let wdw = conn
.intern_atom_immediate_async("WM_DELETE_WINDOW".to_owned(), false)
.await?;
win.set_wm_protocols_async(&mut conn, &[wdw]).await?;
loop {
let directive = async {
MainLoopDirective::ProcessingChannel(
receiver
.recv()
.await
.expect("Sender shouldn't be dropped before receiver"),
)
}
.or(async { MainLoopDirective::ProcessingEvent(conn.wait_for_event_async().await) })
.await;
match directive {
MainLoopDirective::ProcessingChannel(new_color) => {
gc.change_async(
&mut conn,
GcParameters {
foreground: Some(rgb(new_color[0], new_color[1], new_color[2])),
..Default::default()
},
)
.await?;
conn.send_event_async(
win,
EventMask::EXPOSURE,
Event::Expose(ExposeEvent {
window: win,
x: 0,
y: 0,
width,
height,
count: 0,
..Default::default()
}),
)
.await?;
}
MainLoopDirective::ProcessingEvent(ev) => match ev? {
Event::ClientMessage(cme) => {
if cme.data.longs()[0] == wdw.xid {
break;
}
}
Event::Expose(_) => {
gc.fill_rectangle_async(
&mut conn,
win,
Rectangle {
x: 0,
y: 0,
width,
height,
},
)
.await?;
}
Event::ConfigureNotify(cne) => {
width = cne.width;
height = cne.height;
}
_ => (),
},
}
}
receiver.close();
Ok(())
}
async fn entry(ex: &Executor<'_>) -> breadx::Result<()> {
let (sender, receiver) = unbounded::<[u8; 3]>();
let co_rng = ex.spawn(rng(sender));
let co_x11 = ex.spawn(x_process(receiver));
let ((), x11_res) = future::zip(co_rng, co_x11).await;
x11_res
}
fn main() -> breadx::Result<()> {
let (signal, shutdown) = unbounded::<()>();
let ex = Executor::new();
Parallel::new()
.each(0..2, |_| async_io::block_on(ex.run(shutdown.recv())))
.finish(|| {
let res = async_io::block_on(entry(&ex));
mem::drop(signal);
res
})
.1
}