wl_client/lib.rs
1//! This crate provides safe wrappers around libwayland. It supports both blocking and
2//! async operations with any async runtime. Both `'static` and `'scoped`, `Send` and
3//! `!Send` event handlers are supported.
4//!
5//! The most important types provided by this crate are
6//!
7//! - [`Libwayland`]: A reference to a dynamically loaded `libwayland-client.so`.
8//! - [`Connection`]: A connection to a wayland compositor.
9//! - [`Queue`]/[`QueueWithData`]: An event queue.
10//!
11//! This crate does not itself provide type-safe wrappers for wayland protocol objects
12//! (`wl_display`, `wl_keyboard`, etc). Instead, applications should use the
13//! [`wl-client-builder`] crate to generate these wrappers ahead of time or in `build.rs`.
14//!
15//! [`wl-client-builder`]: https://docs.rs/wl-client-builder
16//!
17//! # Example: Hello wayland
18//!
19//! The code of this example can be found in the `hello-wayland` example binary.
20//!
21//! ```
22//! # use wl_client::{proxy, Libwayland};
23//! # use wl_client::test_protocols::core::wl_callback::WlCallback;
24//! # use wl_client::test_protocols::core::wl_display::WlDisplay;
25//! #
26//! // Load the `libwayland-client.so` dynamic library.
27//! let lib = Libwayland::open().unwrap();
28//! // Connect to the default display determined by the `WAYLAND_DISPLAY` env var.
29//! let con = lib.connect_to_default_display().unwrap();
30//! // Create a new event queue with the name `hello-wayland`. This name will show up
31//! // when debugging applications with `WAYLAND_DEBUG=1`.
32//! let queue = con.create_queue(c"hello-wayland");
33//! // Get a reference to the `wl_display` singleton. This type was generated with the
34//! // `wl-client-builder` crate.
35//! let display: WlDisplay = queue.display();
36//! // Create a `wl_callback` object. The compositor will immediately respond with a
37//! // `wl_callback.done` event.
38//! let sync = display.sync();
39//! // Set the event handler of the proxy.
40//! proxy::set_event_handler(
41//! &sync,
42//! // When only handling a single event type, the following functional form can be
43//! // used. In general, and when handling more than one event type, the event handler
44//! // trait must be implemented. In this case, `WlCallbackEventHandler`.
45//! WlCallback::on_done(|_, _| println!("Hello wayland!")),
46//! );
47//! // Perform a roundtrip to ensure that the `done` event has been dispatched.
48//! queue.dispatch_roundtrip_blocking().unwrap();
49//! ```
50//!
51//! # Example: Getting a registry snapshot
52//!
53//! The code of this example can be found in the `get-registry` example binary.
54//!
55//! ```
56//! # use parking_lot::Mutex;
57//! # use wl_client::Queue;
58//! # use wl_client::test_protocols::core::wl_display::WlDisplay;
59//! # use wl_client::test_protocols::core::wl_registry::WlRegistry;
60//! #
61//! struct Global {
62//! pub name: u32,
63//! pub interface: String,
64//! pub version: u32,
65//! }
66//!
67//! fn get_registry_snapshot(queue: &Queue) -> (WlRegistry, Vec<Global>) {
68//! // Create a new registry that will receive the globals and can later be used to
69//! // bind them.
70//! let registry = queue.display::<WlDisplay>().get_registry();
71//! let globals = Mutex::new(vec![]);
72//! // Since we don't care about registry events after this function returns, we can
73//! // use a dispatch scope. The event handlers in this scope will not be called after
74//! // the function returns.
75//! queue.dispatch_scope_blocking(|scope| {
76//! scope.set_event_handler(
77//! ®istry,
78//! // Since we only want to create a snapshot, we don't care about
79//! // global_remove events. This allows us to use the functional event handler
80//! // form.
81//! WlRegistry::on_global(|_, name, interface, version| {
82//! globals.lock().push(Global {
83//! name,
84//! interface: interface.to_string(),
85//! version,
86//! });
87//! }),
88//! );
89//! queue.dispatch_roundtrip_blocking().unwrap();
90//! });
91//! // The event handler will no longer be called after this function returns but
92//! // the registry can still be used to bind globals.
93//! (registry, globals.into_inner())
94//! }
95//! ```
96//!
97//! # Example: Passing mutable state to event handlers
98//!
99//! The code of this example can be found in the `get-registry-with-data` example binary.
100//!
101//! ```
102//! # use wl_client::{proxy, Libwayland};
103//! # use wl_client::test_protocols_data::core::wl_display::WlDisplay;
104//! # use wl_client::test_protocols_data::core::wl_registry::WlRegistry;
105//! #
106//! struct State {
107//! registry: WlRegistry,
108//! globals: Vec<Global>,
109//! }
110//!
111//! #[expect(dead_code)]
112//! #[derive(Debug)]
113//! struct Global {
114//! name: u32,
115//! interface: String,
116//! version: u32,
117//! }
118//!
119//! # fn f() {
120//! let lib = Libwayland::open().unwrap();
121//! let con = lib.connect_to_default_display().unwrap();
122//! let (_queue, queue) = con.create_queue_with_data::<State>(c"get-registry");
123//!
124//! // Create a new registry that will receive the globals and can later be used to
125//! // bind them.
126//! let mut state = State {
127//! registry: queue.display::<WlDisplay>().get_registry(),
128//! globals: vec![],
129//! };
130//!
131//! // Since we only want to create a snapshot, we don't care about
132//! // global_remove events. This allows us to use the functional event handler
133//! // form.
134//! proxy::set_event_handler(
135//! &state.registry,
136//! WlRegistry::on_global(|state: &mut State, _, name, interface, version| {
137//! state.globals.push(Global {
138//! name,
139//! interface: interface.to_string(),
140//! version,
141//! });
142//! }),
143//! );
144//! queue.dispatch_roundtrip_blocking(&mut state).unwrap();
145//!
146//! println!("{:#?}", state.globals);
147//! # }
148//! ```
149//!
150//! # Example: Handling keyboard events
151//!
152//! The code of this example can be found in the `keyboard-events` example binary.
153//!
154//! ```
155//! # use std::cell::RefCell;
156//! # use std::rc::Rc;
157//! # use wl_client::proxy;
158//! # use wl_client::test_protocols::core::wl_keyboard::{WlKeyboard, WlKeyboardEventHandler, WlKeyboardKeyState, WlKeyboardRef};
159//! # use wl_client::test_protocols::core::wl_seat::{WlSeat, WlSeatCapability, WlSeatEventHandler, WlSeatRef};
160//! #
161//! /// The state used to handle seat and keyboard events. In a real application this
162//! /// would likely also contain a way to map keycodes to keysyms and to forward events
163//! /// to the rest of the application.
164//! struct Seat {
165//! wl_seat: WlSeat,
166//! wl_keyboard: RefCell<Option<WlKeyboard>>,
167//! }
168//!
169//! #[derive(Clone)]
170//! struct SeatEventHandler(Rc<Seat>);
171//!
172//! impl WlSeatEventHandler for SeatEventHandler {
173//! fn capabilities(&self, _slf: &WlSeatRef, capabilities: WlSeatCapability) {
174//! let kb = &mut *self.0.wl_keyboard.borrow_mut();
175//! // When the seat loses/gains the keyboard capability, we need to
176//! // destroy/create a wl_keyboard.
177//! if capabilities.contains(WlSeatCapability::KEYBOARD) {
178//! if kb.is_none() {
179//! let wl_keyboard = self.0.wl_seat.get_keyboard();
180//! // Since we're using `Rc` here, event handlers must be set with the
181//! // `_local` function which allows `!Send` event handlers.
182//! proxy::set_event_handler_local(&wl_keyboard, self.clone());
183//! *kb = Some(wl_keyboard);
184//! }
185//! } else {
186//! if let Some(kb) = kb.take() {
187//! // The wl_keyboard.release request is only available since version 3.
188//! // If it's not available, at least destroy the client-side object.
189//! if proxy::version(&*kb) >= WlKeyboard::REQ__RELEASE__SINCE {
190//! kb.release();
191//! } else {
192//! proxy::destroy(&kb);
193//! }
194//! }
195//! }
196//! }
197//! }
198//!
199//! // If more than one event type needs to be handled by an event handler, the convenient
200//! // functional API cannot be used. Instead the application needs to implement the
201//! // `*EventHandler` trait manually.
202//! impl WlKeyboardEventHandler for SeatEventHandler {
203//! fn key(&self,
204//! _slf: &WlKeyboardRef,
205//! _serial: u32,
206//! _time: u32,
207//! key: u32,
208//! state: WlKeyboardKeyState,
209//! ) {
210//! println!("key {key:} {state:?}");
211//! }
212//!
213//! fn modifiers(
214//! &self,
215//! _slf: &WlKeyboardRef,
216//! _serial: u32,
217//! mods_depressed: u32,
218//! mods_latched: u32,
219//! mods_locked: u32,
220//! group: u32,
221//! ) {
222//! println!("modifiers {mods_depressed:x}, {mods_latched:x}, {mods_locked:x}, {group}");
223//! }
224//! }
225//! ```
226//!
227//! # Example: Async roundtrip
228//!
229//! The code of this example can be found in the `async-dispatch` example binary.
230//!
231//! ```
232//! # use std::cell::Cell;
233//! # use wl_client::Libwayland;
234//! # use wl_client::test_protocols::core::wl_display::WlDisplay;
235//! # use wl_client::test_protocols::core::wl_registry::WlRegistry;
236//! #
237//! # async fn async_roundtrip() {
238//! let lib = Libwayland::open().unwrap();
239//! let con = lib.connect_to_default_display().unwrap();
240//! let queue = con.create_local_queue(c"async-roundtrip");
241//! let registry = queue.display::<WlDisplay>().get_registry();
242//! let num_globals = Cell::new(0);
243//! queue
244//! .dispatch_scope_async(async |scope| {
245//! scope.set_event_handler_local(
246//! ®istry,
247//! WlRegistry::on_global(|_, _, _, _| {
248//! num_globals.set(num_globals.get() + 1);
249//! }),
250//! );
251//! // This function can be used to perform an async roundtrip. It is
252//! // compatible with any async runtime. This example also demonstrates
253//! // that this works in combination with scoped event handlers.
254//! queue.dispatch_roundtrip_async().await.unwrap();
255//! })
256//! .await;
257//! println!("number of globals: {}", num_globals.get());
258//! # }
259//! ```
260//!
261//! # Example: Async waiting for events
262//!
263//! The code of this example can be found in the `async-wait` example binary.
264//!
265//! ```
266//! # use wl_client::{proxy, Libwayland};
267//! # use wl_client::test_protocols::core::wl_callback::WlCallback;
268//! # use wl_client::test_protocols::core::wl_display::WlDisplay;
269//! #
270//! # async fn wait_for_events() {
271//! let lib = Libwayland::open().unwrap();
272//! let con = lib.connect_to_default_display().unwrap();
273//! let queue = con.create_local_queue(c"async-wait");
274//!
275//! let sync = queue.display::<WlDisplay>().sync();
276//! proxy::set_event_handler(&sync, WlCallback::on_done(|_, _| println!("done!")));
277//!
278//! loop {
279//! // This future completes once there are events to dispatch in the queue.
280//! queue.wait_for_events().await.unwrap();
281//! queue.dispatch_pending().unwrap();
282//! }
283//! # }
284//! ```
285//!
286//! # Example: Poll-based event loop integration
287//!
288//! The code of this example can be found in the `poll-integration` example binary.
289//!
290//! ```
291//! # use std::os::fd::AsRawFd;
292//! # use mio::{Interest, Token};
293//! # use mio::unix::SourceFd;
294//! # use wl_client::{proxy, Libwayland};
295//! # use wl_client::test_protocols::core::wl_callback::WlCallback;
296//! # use wl_client::test_protocols::core::wl_display::WlDisplay;
297//! #
298//! # fn event_loop() {
299//! let lib = Libwayland::open().unwrap();
300//! let con = lib.connect_to_default_display().unwrap();
301//! let queue = con.create_local_queue(c"poll-integration");
302//!
303//! // The watcher exposes a file descriptor that will become readable when the queue
304//! // has new events.
305//! let watcher = queue.create_watcher().unwrap();
306//! let token = Token(0);
307//!
308//! let sync = queue.display::<WlDisplay>().sync();
309//! proxy::set_event_handler(&sync, WlCallback::on_done(|_, _| println!("done!")));
310//!
311//! let mut events = mio::Events::with_capacity(2);
312//! let mut poll = mio::Poll::new().unwrap();
313//! poll.registry()
314//! .register(
315//! &mut SourceFd(&watcher.as_raw_fd()),
316//! token,
317//! Interest::READABLE,
318//! )
319//! .unwrap();
320//!
321//! loop {
322//! // Flush requests before polling.
323//! con.flush().unwrap();
324//! poll.poll(&mut events, None).unwrap();
325//! for event in events.iter() {
326//! if event.token() == token {
327//! queue.dispatch_pending().unwrap();
328//! // Reset the watcher to clear the readability status.
329//! watcher.reset().unwrap();
330//! }
331//! }
332//! events.clear();
333//! }
334//! # }
335//! ```
336
337#![allow(clippy::len_zero)]
338
339pub use {
340 connection::{Connection, wait_for_events::QueueWatcher},
341 fixed::Fixed,
342 libwayland::Libwayland,
343 proxy::low_level::owned::scope::Scope,
344 queue::{BorrowedQueue, DispatchLock, Queue, QueueOwner, QueueWithData},
345};
346
347#[doc(hidden)]
348pub mod builder;
349mod connection;
350pub mod ffi;
351mod fixed;
352#[cfg_attr(any(test, feature = "_doctests"), path = "libwayland_test.rs")]
353mod libwayland;
354mod protocols;
355pub mod proxy;
356mod queue;
357#[cfg(any(test, feature = "_doctests"))]
358pub mod test_protocol_helpers;
359#[cfg(any(test, feature = "_doctests"))]
360pub mod test_protocols;
361#[cfg(any(test, feature = "_doctests"))]
362pub mod test_protocols_data;
363#[cfg(test)]
364mod tests;
365mod utils;