wayland_window/lib.rs
1//! Wayland Window, a minimalistic decoration-drawing library for
2//! wayland applications.
3//!
4//! This crate is only usable in conjuction of the
5//! [`wayland-client`](https://crates.io/crates/wayland-client) crate.
6//!
7//! ## Creating a window with decorations
8//!
9//! Creating a decorated frame for your window is simply done using the provided init function:
10//!
11//! ```ignore
12//! use wayland_window::create_frame;
13//! // if using the legacy wl_shell global
14//! let shell = Shell::Wl(my_wl_shell);
15//! // if using the new not-yet-stable xdg_shell
16//! let shell = Shell::Xdg(my_xdh_shell);
17//! let frame = create_frame(
18//! &mut event_queue, my_implementation, my_implementation_data,
19//! &my_surface, width, height, &compositor, &subcompositor, &shm, &shell, Some(seat)
20//! ).unwrap(); // creation can fail
21//! ```
22//!
23//! As you can see, you need to pass several references to global objects as well as a `WlSeat`.
24//! It is required for the library to be able to create the surfaces to draw the borders, react
25//! to user input in the borders, for resizing and move. It will use the events provided on the
26//! seat you passed as argument. (So if you are on a setup with more than one pointer,
27//! only the one associated with this seat will be able to resize the window).
28//!
29//! See next section for example use of the `my_implementation` and
30//! `my_implementation_data` arguments.
31//!
32//! ## Configure events
33//!
34//! The `Frame` object will not resize your window itself, as it cannot do it.
35//!
36//! When the user clicks on a border and starts a resize, the server will start to generate a
37//! number of `configure` events on the shell surface. You'll need to process the events generated
38//! by the surface to handle them.
39//!
40//! The wayland server can (and will) generate a ton of `configure` events during a single
41//! `WlDisplay::dispatch()` if the user is currently resizing the window. You are only required to
42//! process the last one, and if you try to handle them all your aplication will be very
43//! laggy.
44//!
45//! The proper way is to accumulate them in your subhandler, overwriting the the previous one
46//! each time, and manually checking if one has been received in the main loop of your program.
47//! For example like this
48//!
49//! ```no_run
50//! # extern crate wayland_client;
51//! # extern crate wayland_window;
52//! use wayland_window::{Frame, create_frame, FrameImplementation};
53//!
54//! // define a state to accumulate sizes
55//! struct ConfigureState {
56//! new_size: Option<(i32,i32)>
57//! }
58//!
59//! # fn main() {
60//! # let (display, mut event_queue) = wayland_client::default_connect().unwrap();
61//! // insert it in your event queue state
62//! let configure_token = event_queue.state().insert(ConfigureState { new_size: None });
63//!
64//! // use it in your implementation:
65//! let my_implementation = FrameImplementation {
66//! configure: |evqh, token, _, newsize| {
67//! let configure_state: &mut ConfigureState = evqh.state().get_mut(token);
68//! configure_state.new_size = newsize;
69//! },
70//! close: |_, _| { /* ... */ },
71//! refresh: |_, _| { /* ... */ }
72//! };
73//!
74//! # let (my_surface,width,height,compositor,subcompositor,shm,shell,seat) = unimplemented!();
75//! // create the decorated surface:
76//! let frame = create_frame(
77//! &mut event_queue, // the event queue
78//! my_implementation, // our implementation
79//! configure_token.clone(), // the implementation data
80//! &my_surface, width, height, &compositor, &subcompositor, &shm, &shell, Some(seat)
81//! ).unwrap();
82//!
83//! // then, while running your event loop
84//! loop {
85//! display.flush().unwrap();
86//! event_queue.dispatch().unwrap();
87//!
88//! // check if a resize is needed
89//! let mut configure_state = event_queue.state().get_mut(&configure_token);
90//! if let Some((w, h)) = configure_state.new_size.take() {
91//! // The compositor suggests we take a new size of (w, h)
92//! // Handle it as needed (see next section)
93//! }
94//! }
95//!
96//! # }
97//! ```
98//!
99//! ## Resizing the surface
100//!
101//! When resizing your main surface, you need to tell the `Frame` that it
102//! must update its dimensions. This is very simple:
103//!
104//! ```ignore
105//! // update your contents size (here by attaching a new buffer)
106//! surface.attach(Some(&new_buffer));
107//! surface.commit();
108//! // update the borders size
109//! frame.resize(width, height);
110//! // refresh the frame so that it actually draws the new size
111//! frame.refresh();
112//! ```
113//!
114//! If you do this as a response of a `configure` event, note the following points:
115//!
116//! - You do not have to respect the exact sizes provided by the compositor, it is
117//! just a hint. You can even ignore it if you don't want the window to be resized.
118//! - In case you chose to ignore the resize, it can be appropiate to still resize your
119//! window to its current size (update the buffer to the compositor), as the compositer
120//! might have resized your window without telling you.
121//! - The size hint provided to your implementation is a size hint for the interior of the
122//! window: the dimensions of the border has been subtracted from the hint the compositor
123//! gave. If you need to compute dimensions taking into account the sizes of the borders,
124//! you can use the `add_borders` and `subtract_borders` functions.
125
126#![warn(missing_docs)]
127
128extern crate memmap;
129extern crate tempfile;
130extern crate wayland_client;
131extern crate wayland_protocols;
132
133mod frame;
134mod pointer;
135mod theme;
136mod themed_pointer;
137mod shell;
138
139pub use frame::{Frame, State};
140use pointer::{Pointer, PointerState};
141pub use shell::{Configure, Shell};
142use std::cell::RefCell;
143use std::rc::Rc;
144use std::sync::{Arc, Mutex};
145pub use theme::{add_borders, subtract_borders};
146use themed_pointer::ThemedPointer;
147use wayland_client::{EventQueueHandle, Proxy};
148use wayland_client::protocol::*;
149
150#[derive(Debug, Copy, Clone, PartialEq, Eq)]
151pub(crate) enum Location {
152 None,
153 Top,
154 TopRight,
155 Right,
156 BottomRight,
157 Bottom,
158 BottomLeft,
159 Left,
160 TopLeft,
161 TopBar,
162 Inside,
163 Button(UIButton),
164}
165
166#[derive(Debug, Copy, Clone, PartialEq, Eq)]
167pub(crate) enum UIButton {
168 Minimize,
169 Maximize,
170 Close,
171}
172
173pub(crate) struct FrameIData<ID> {
174 pub(crate) implementation: FrameImplementation<ID>,
175 pub(crate) meta: Arc<Mutex<::frame::FrameMetadata>>,
176 pub(crate) idata: Rc<RefCell<ID>>,
177}
178
179pub(crate) struct PointerIData<ID> {
180 pub(crate) implementation: FrameImplementation<ID>,
181 pub(crate) pstate: PointerState,
182 pub(crate) idata: Rc<RefCell<ID>>,
183}
184
185impl<ID> Clone for FrameIData<ID> {
186 fn clone(&self) -> FrameIData<ID> {
187 FrameIData {
188 implementation: self.implementation.clone(),
189 meta: self.meta.clone(),
190 idata: self.idata.clone(),
191 }
192 }
193}
194
195/// For handling events that occur to a Frame.
196pub struct FrameImplementation<ID> {
197 /// Called whenever the Frame has been resized.
198 ///
199 /// **Note:** if you've not set a minimum size, `width` and `height` will not always be
200 /// positive values. Values can be negative if a user attempts to resize the window past
201 /// the left or top borders.
202 pub configure:
203 fn(evqh: &mut EventQueueHandle, idata: &mut ID, cfg: shell::Configure, newsize: Option<(i32, i32)>),
204 /// Called when the Frame is closed.
205 pub close: fn(evqh: &mut EventQueueHandle, idata: &mut ID),
206 /// Called when the Frame wants to be refreshed
207 pub refresh: fn(evqh: &mut EventQueueHandle, idata: &mut ID),
208}
209
210impl<ID> Copy for FrameImplementation<ID> {}
211impl<ID> Clone for FrameImplementation<ID> {
212 fn clone(&self) -> FrameImplementation<ID> {
213 *self
214 }
215}
216
217/// Create a decoration frame for a wl_surface
218///
219/// This will create a decoration and declare it as a shell surface to
220/// the wayland compositor.
221///
222/// See crate documentations for details about how to use it.
223pub fn create_frame<ID: 'static>(evqh: &mut EventQueueHandle, implementation: FrameImplementation<ID>,
224 idata: ID, surface: &wl_surface::WlSurface, width: i32, height: i32,
225 compositor: &wl_compositor::WlCompositor,
226 subcompositor: &wl_subcompositor::WlSubcompositor, shm: &wl_shm::WlShm,
227 shell: &Shell, seat: Option<wl_seat::WlSeat>)
228 -> Result<Frame, ()> {
229 // create the frame
230 let mut frame = Frame::new(
231 surface,
232 width,
233 height,
234 compositor,
235 subcompositor,
236 shm,
237 shell,
238 )?;
239
240 let frame_idata = FrameIData {
241 implementation: implementation,
242 meta: frame.meta.clone(),
243 idata: Rc::new(RefCell::new(idata)),
244 };
245
246 // create the pointer
247 if let Some(seat) = seat {
248 let pointer = seat.get_pointer().expect("Received a defunct seat.");
249 frame.pointer = pointer.clone();
250 let pointer = ThemedPointer::load(pointer, None, &compositor, &shm)
251 .map(Pointer::Themed)
252 .unwrap_or_else(Pointer::Plain);
253 let pstate = PointerState::new(
254 frame.meta.clone(),
255 pointer,
256 frame.surface.clone().unwrap(),
257 frame.shell_surface.clone().unwrap(),
258 seat,
259 );
260 let pointer_idata = PointerIData {
261 implementation: implementation,
262 pstate: pstate,
263 idata: frame_idata.idata.clone(),
264 };
265 evqh.register(
266 frame.pointer.as_ref().unwrap(),
267 ::pointer::pointer_implementation(),
268 pointer_idata,
269 );
270 }
271
272 frame.shell_surface.register_to(evqh, frame_idata);
273
274 Ok(frame)
275}