Skip to content

Commit

Permalink
wayland-client: Add example for usage with tokio
Browse files Browse the repository at this point in the history
  • Loading branch information
mcoffin committed Feb 22, 2024
1 parent e7f256d commit 7447dda
Show file tree
Hide file tree
Showing 2 changed files with 262 additions and 1 deletion.
9 changes: 8 additions & 1 deletion wayland-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,20 @@ keywords = ["wayland", "client"]
description = "Bindings to the standard C implementation of the wayland protocol, client side."
readme = "README.md"

[[example]]
name = "list_globals"

[[example]]
name = "simple_window_async"
required-features = ["tokio"]

[dependencies]
wayland-backend = { version = "0.3.1", path = "../wayland-backend" }
wayland-scanner = { version = "0.31.0", path = "../wayland-scanner" }
bitflags = "2"
rustix = { version = "0.38.0", features = ["event"] }
log = { version = "0.4", optional = true }
tokio = { version = "1", optional = true, default-features = false, features = ["net"] }
tokio = { version = "1", optional = true, default-features = false, features = ["net", "rt"] }

[dev-dependencies]
wayland-protocols = { path = "../wayland-protocols", features = ["client"] }
Expand Down
254 changes: 254 additions & 0 deletions wayland-client/examples/simple_window_async.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
use std::{
error::Error,
fs::File,
os::unix::prelude::AsFd,
process,
};

use wayland_client::{
delegate_noop,
protocol::{
wl_buffer, wl_compositor, wl_keyboard, wl_registry, wl_seat, wl_shm, wl_shm_pool,
wl_surface,
},
Connection, Dispatch, QueueHandle, WEnum,
ConnectionExtAsync,
};

use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};

async fn real_main() -> Result<(), Box<dyn Error + 'static>> {
let conn = Connection::connect_to_env()?;

let mut event_queue = conn.new_async_event_queue()?;
let qhandle = event_queue.queue.handle();

let display = conn.display();
display.get_registry(&qhandle, ());

let mut state = State {
running: true,
base_surface: None,
buffer: None,
wm_base: None,
xdg_surface: None,
configured: false,
};

println!("Starting the example window app, press <ESC> to quit.");

while state.running {
event_queue.dispatch_single(&mut state).await?;
}

Ok(())
}

fn main() {
use tokio::runtime::Builder;
// Create the runtime here so that we don't force tokio/macros feature on downstream users
let rt = Builder::new_current_thread()
.enable_io()
.build()
.expect("Failed to create tokio runtime");
rt.block_on(async move {
if let Err(e) = real_main().await {
panic!("{}", &e);
}
});
}

struct State {
running: bool,
base_surface: Option<wl_surface::WlSurface>,
buffer: Option<wl_buffer::WlBuffer>,
wm_base: Option<xdg_wm_base::XdgWmBase>,
xdg_surface: Option<(xdg_surface::XdgSurface, xdg_toplevel::XdgToplevel)>,
configured: bool,
}

impl Dispatch<wl_registry::WlRegistry, ()> for State {
fn event(
state: &mut Self,
registry: &wl_registry::WlRegistry,
event: wl_registry::Event,
_: &(),
_: &Connection,
qh: &QueueHandle<Self>,
) {
if let wl_registry::Event::Global { name, interface, .. } = event {
match &interface[..] {
"wl_compositor" => {
let compositor =
registry.bind::<wl_compositor::WlCompositor, _, _>(name, 1, qh, ());
let surface = compositor.create_surface(qh, ());
state.base_surface = Some(surface);

if state.wm_base.is_some() && state.xdg_surface.is_none() {
state.init_xdg_surface(qh);
}
}
"wl_shm" => {
let shm = registry.bind::<wl_shm::WlShm, _, _>(name, 1, qh, ());

let (init_w, init_h) = (320, 240);

let mut file = tempfile::tempfile().unwrap();
draw(&mut file, (init_w, init_h));
let pool = shm.create_pool(file.as_fd(), (init_w * init_h * 4) as i32, qh, ());
let buffer = pool.create_buffer(
0,
init_w as i32,
init_h as i32,
(init_w * 4) as i32,
wl_shm::Format::Argb8888,
qh,
(),
);
state.buffer = Some(buffer.clone());

if state.configured {
let surface = state.base_surface.as_ref().unwrap();
surface.attach(Some(&buffer), 0, 0);
surface.commit();
}
}
"wl_seat" => {
registry.bind::<wl_seat::WlSeat, _, _>(name, 1, qh, ());
}
"xdg_wm_base" => {
let wm_base = registry.bind::<xdg_wm_base::XdgWmBase, _, _>(name, 1, qh, ());
state.wm_base = Some(wm_base);

if state.base_surface.is_some() && state.xdg_surface.is_none() {
state.init_xdg_surface(qh);
}
}
_ => {}
}
}
}
}

// Ignore events from these object types in this example.
delegate_noop!(State: ignore wl_compositor::WlCompositor);
delegate_noop!(State: ignore wl_surface::WlSurface);
delegate_noop!(State: ignore wl_shm::WlShm);
delegate_noop!(State: ignore wl_shm_pool::WlShmPool);
delegate_noop!(State: ignore wl_buffer::WlBuffer);

fn draw(tmp: &mut File, (buf_x, buf_y): (u32, u32)) {
use std::{cmp::min, io::Write};
let mut buf = std::io::BufWriter::new(tmp);
for y in 0..buf_y {
for x in 0..buf_x {
let a = 0xFF;
let r = min(((buf_x - x) * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
let g = min((x * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
let b = min(((buf_x - x) * 0xFF) / buf_x, (y * 0xFF) / buf_y);
buf.write_all(&[b as u8, g as u8, r as u8, a as u8]).unwrap();
}
}
buf.flush().unwrap();
}

impl State {
fn init_xdg_surface(&mut self, qh: &QueueHandle<State>) {
let wm_base = self.wm_base.as_ref().unwrap();
let base_surface = self.base_surface.as_ref().unwrap();

let xdg_surface = wm_base.get_xdg_surface(base_surface, qh, ());
let toplevel = xdg_surface.get_toplevel(qh, ());
toplevel.set_title("A fantastic window!".into());

base_surface.commit();

self.xdg_surface = Some((xdg_surface, toplevel));
}
}

impl Dispatch<xdg_wm_base::XdgWmBase, ()> for State {
fn event(
_: &mut Self,
wm_base: &xdg_wm_base::XdgWmBase,
event: xdg_wm_base::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
if let xdg_wm_base::Event::Ping { serial } = event {
wm_base.pong(serial);
}
}
}

impl Dispatch<xdg_surface::XdgSurface, ()> for State {
fn event(
state: &mut Self,
xdg_surface: &xdg_surface::XdgSurface,
event: xdg_surface::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
if let xdg_surface::Event::Configure { serial, .. } = event {
xdg_surface.ack_configure(serial);
state.configured = true;
let surface = state.base_surface.as_ref().unwrap();
if let Some(ref buffer) = state.buffer {
surface.attach(Some(buffer), 0, 0);
surface.commit();
}
}
}
}

impl Dispatch<xdg_toplevel::XdgToplevel, ()> for State {
fn event(
state: &mut Self,
_: &xdg_toplevel::XdgToplevel,
event: xdg_toplevel::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
if let xdg_toplevel::Event::Close {} = event {
state.running = false;
}
}
}

impl Dispatch<wl_seat::WlSeat, ()> for State {
fn event(
_: &mut Self,
seat: &wl_seat::WlSeat,
event: wl_seat::Event,
_: &(),
_: &Connection,
qh: &QueueHandle<Self>,
) {
if let wl_seat::Event::Capabilities { capabilities: WEnum::Value(capabilities) } = event {
if capabilities.contains(wl_seat::Capability::Keyboard) {
seat.get_keyboard(qh, ());
}
}
}
}

impl Dispatch<wl_keyboard::WlKeyboard, ()> for State {
fn event(
state: &mut Self,
_: &wl_keyboard::WlKeyboard,
event: wl_keyboard::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
if let wl_keyboard::Event::Key { key, .. } = event {
if key == 1 {
// ESC key
state.running = false;
}
}
}
}

0 comments on commit 7447dda

Please sign in to comment.