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}