Skip to content

Commit

Permalink
Update to calloop 0.12.1 and wayland-rs 0.31.0
Browse files Browse the repository at this point in the history
The update resolves the long present issue of event
source not waking up during the concurrent reads due to
not following the wayland's prepare_read protocol correctly.

In particular, this let's downstream clients, like winit, to remove
workarounds related to mesa doing blocking dispatch and winit
missing wake ups due to that.
  • Loading branch information
DJMcNab authored Sep 20, 2023
1 parent f9b1fef commit 5dfdd54
Show file tree
Hide file tree
Showing 3 changed files with 352 additions and 90 deletions.
10 changes: 7 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ keywords = ["wayland", "windowing"]
rust-version = "1.65.0"

[dependencies]
wayland-client = "0.30.2"
wayland-backend = "0.1.0"
calloop = "0.10.0"
wayland-client = "0.31.1"
wayland-backend = "0.3.0"
calloop = "0.12.1"
log = { version = "0.4.19", optional = true }
rustix = { version = "0.38.4", default-features = false, features = ["std"] }

[dev-dependencies]
tempfile = "3.8.0"
wayland-protocols = { version = "0.31.0", features = ["client"] }
240 changes: 240 additions & 0 deletions examples/simplest-window.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
use std::fs::File;
use std::os::unix::prelude::AsFd;

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

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

fn main() {
let conn = Connection::connect_to_env().unwrap();

let event_queue = conn.new_event_queue();
let qhandle = event_queue.handle();

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

let mut event_loop: EventLoop<State> =
EventLoop::try_new().expect("Failed to initialize the event loop!");
let loop_handle = event_loop.handle();
WaylandSource::new(conn, event_queue).insert(loop_handle).unwrap();
let stop_handle = event_loop.get_signal();

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

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

event_loop.run(None, &mut state, |_| {}).unwrap();
}

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,
stop_handle: calloop::LoopSignal,
}

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;
use std::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);

let color = (a << 24) + (r << 16) + (g << 8) + b;
buf.write_all(&color.to_ne_bytes()).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.stop_handle.stop();
}
}
}
}
Loading

0 comments on commit 5dfdd54

Please sign in to comment.