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`]: 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: Handling keyboard events
98//!
99//! The code of this example can be found in the `keyboard-events` example binary.
100//!
101//! ```
102//! # use std::cell::RefCell;
103//! # use std::rc::Rc;
104//! # use wl_client::proxy;
105//! # use wl_client::test_protocols::core::wl_keyboard::{WlKeyboard, WlKeyboardEventHandler, WlKeyboardKeyState, WlKeyboardRef};
106//! # use wl_client::test_protocols::core::wl_seat::{WlSeat, WlSeatCapability, WlSeatEventHandler, WlSeatRef};
107//! #
108//! /// The state used to handle seat and keyboard events. In a real application this
109//! /// would likely also contain a way to map keycodes to keysyms and to forward events
110//! /// to the rest of the application.
111//! struct Seat {
112//! wl_seat: WlSeat,
113//! wl_keyboard: RefCell<Option<WlKeyboard>>,
114//! }
115//!
116//! #[derive(Clone)]
117//! struct SeatEventHandler(Rc<Seat>);
118//!
119//! impl WlSeatEventHandler for SeatEventHandler {
120//! fn capabilities(&self, _slf: &WlSeatRef, capabilities: WlSeatCapability) {
121//! let kb = &mut *self.0.wl_keyboard.borrow_mut();
122//! // When the seat loses/gains the keyboard capability, we need to
123//! // destroy/create a wl_keyboard.
124//! if capabilities.contains(WlSeatCapability::KEYBOARD) {
125//! if kb.is_none() {
126//! let wl_keyboard = self.0.wl_seat.get_keyboard();
127//! // Since we're using `Rc` here, event handlers must be set with the
128//! // `_local` function which allows `!Send` event handlers.
129//! proxy::set_event_handler_local(&wl_keyboard, self.clone());
130//! *kb = Some(wl_keyboard);
131//! }
132//! } else {
133//! if let Some(kb) = kb.take() {
134//! // The wl_keyboard.release request is only available since version 3.
135//! // If it's not available, at least destroy the client-side object.
136//! if proxy::version(&*kb) >= WlKeyboard::REQ__RELEASE__SINCE {
137//! kb.release();
138//! } else {
139//! proxy::destroy(&kb);
140//! }
141//! }
142//! }
143//! }
144//! }
145//!
146//! // If more than one event type needs to be handled by an event handler, the convenient
147//! // functional API cannot be used. Instead the application needs to implement the
148//! // `*EventHandler` trait manually.
149//! impl WlKeyboardEventHandler for SeatEventHandler {
150//! fn key(&self,
151//! _slf: &WlKeyboardRef,
152//! _serial: u32,
153//! _time: u32,
154//! key: u32,
155//! state: WlKeyboardKeyState,
156//! ) {
157//! println!("key {key:} {state:?}");
158//! }
159//!
160//! fn modifiers(
161//! &self,
162//! _slf: &WlKeyboardRef,
163//! _serial: u32,
164//! mods_depressed: u32,
165//! mods_latched: u32,
166//! mods_locked: u32,
167//! group: u32,
168//! ) {
169//! println!("modifiers {mods_depressed:x}, {mods_latched:x}, {mods_locked:x}, {group}");
170//! }
171//! }
172//! ```
173//!
174//! # Example: Async roundtrip
175//!
176//! The code of this example can be found in the `async-dispatch` example binary.
177//!
178//! ```
179//! # use std::cell::Cell;
180//! # use wl_client::Libwayland;
181//! # use wl_client::test_protocols::core::wl_display::WlDisplay;
182//! # use wl_client::test_protocols::core::wl_registry::WlRegistry;
183//! #
184//! # async fn async_roundtrip() {
185//! let lib = Libwayland::open().unwrap();
186//! let con = lib.connect_to_default_display().unwrap();
187//! let queue = con.create_local_queue(c"async-roundtrip");
188//! let registry = queue.display::<WlDisplay>().get_registry();
189//! let num_globals = Cell::new(0);
190//! queue
191//! .dispatch_scope_async(async |scope| {
192//! scope.set_event_handler_local(
193//! ®istry,
194//! WlRegistry::on_global(|_, _, _, _| {
195//! num_globals.set(num_globals.get() + 1);
196//! }),
197//! );
198//! // This function can be used to perform an async roundtrip. It is
199//! // compatible with any async runtime. This example also demonstrates
200//! // that this works in combination with scoped event handlers.
201//! queue.dispatch_roundtrip_async().await.unwrap();
202//! })
203//! .await;
204//! println!("number of globals: {}", num_globals.get());
205//! # }
206//! ```
207//!
208//! # Example: Async waiting for events
209//!
210//! The code of this example can be found in the `async-wait` example binary.
211//!
212//! ```
213//! # use wl_client::{proxy, Libwayland};
214//! # use wl_client::test_protocols::core::wl_callback::WlCallback;
215//! # use wl_client::test_protocols::core::wl_display::WlDisplay;
216//! #
217//! # async fn wait_for_events() {
218//! let lib = Libwayland::open().unwrap();
219//! let con = lib.connect_to_default_display().unwrap();
220//! let queue = con.create_local_queue(c"async-wait");
221//!
222//! let sync = queue.display::<WlDisplay>().sync();
223//! proxy::set_event_handler(&sync, WlCallback::on_done(|_, _| println!("done!")));
224//!
225//! loop {
226//! // This future completes once there are events to dispatch in the queue.
227//! queue.wait_for_events().await.unwrap();
228//! queue.dispatch_pending().unwrap();
229//! }
230//! # }
231//! ```
232//!
233//! # Example: Poll-based event loop integration
234//!
235//! The code of this example can be found in the `poll-integration` example binary.
236//!
237//! ```
238//! # use std::os::fd::AsRawFd;
239//! # use mio::{Interest, Token};
240//! # use mio::unix::SourceFd;
241//! # use wl_client::{proxy, Libwayland};
242//! # use wl_client::test_protocols::core::wl_callback::WlCallback;
243//! # use wl_client::test_protocols::core::wl_display::WlDisplay;
244//! #
245//! # fn event_loop() {
246//! let lib = Libwayland::open().unwrap();
247//! let con = lib.connect_to_default_display().unwrap();
248//! let queue = con.create_local_queue(c"poll-integration");
249//!
250//! // The watcher exposes a file descriptor that will become readable when the queue
251//! // has new events.
252//! let watcher = queue.create_watcher().unwrap();
253//! let token = Token(0);
254//!
255//! let sync = queue.display::<WlDisplay>().sync();
256//! proxy::set_event_handler(&sync, WlCallback::on_done(|_, _| println!("done!")));
257//!
258//! let mut events = mio::Events::with_capacity(2);
259//! let mut poll = mio::Poll::new().unwrap();
260//! poll.registry()
261//! .register(
262//! &mut SourceFd(&watcher.as_raw_fd()),
263//! token,
264//! Interest::READABLE,
265//! )
266//! .unwrap();
267//!
268//! loop {
269//! // Flush requests before polling.
270//! con.flush().unwrap();
271//! poll.poll(&mut events, None).unwrap();
272//! for event in events.iter() {
273//! if event.token() == token {
274//! queue.dispatch_pending().unwrap();
275//! // Reset the watcher to clear the readability status.
276//! watcher.reset().unwrap();
277//! }
278//! }
279//! events.clear();
280//! }
281//! # }
282//! ```
283
284#![allow(clippy::len_zero)]
285
286pub use {
287 connection::{Connection, wait_for_events::QueueWatcher},
288 fixed::Fixed,
289 libwayland::Libwayland,
290 proxy::low_level::owned::scope::Scope,
291 queue::{BorrowedQueue, DispatchLock, Queue, QueueOwner},
292};
293
294#[doc(hidden)]
295pub mod builder;
296mod connection;
297pub mod ffi;
298mod fixed;
299#[cfg_attr(any(test, feature = "_doctests"), path = "libwayland_test.rs")]
300mod libwayland;
301mod protocols;
302pub mod proxy;
303mod queue;
304#[cfg(any(test, feature = "_doctests"))]
305pub mod test_protocol_helpers;
306#[cfg(any(test, feature = "_doctests"))]
307pub mod test_protocols;
308#[cfg(test)]
309mod tests;
310mod utils;