1 //! Window abstraction
2 use std::cell::RefCell;
3 use std::rc::Rc;
4 use std::sync::Arc;
5 
6 use wayland_client::protocol::{
7     wl_compositor, wl_output, wl_seat, wl_shm, wl_subcompositor, wl_surface,
8 };
9 use wayland_client::{Attached, DispatchData};
10 
11 use wayland_protocols::xdg_shell::client::xdg_toplevel::ResizeEdge;
12 pub use wayland_protocols::xdg_shell::client::xdg_toplevel::State;
13 
14 use wayland_protocols::unstable::xdg_decoration::v1::client::{
15     zxdg_decoration_manager_v1::ZxdgDecorationManagerV1,
16     zxdg_toplevel_decoration_v1::{self, ZxdgToplevelDecorationV1},
17 };
18 
19 use crate::{
20     environment::{Environment, GlobalHandler, MultiGlobalHandler},
21     seat::pointer::ThemeManager,
22     shell,
23 };
24 
25 mod fallback_frame;
26 pub use self::fallback_frame::FallbackFrame;
27 
28 // Defines the minimum window size. Minimum width is set to 2 pixels to circumvent
29 // a bug in mutter - https://gitlab.gnome.org/GNOME/mutter/issues/259
30 const MIN_WINDOW_SIZE: (u32, u32) = (2, 1);
31 
32 /// Represents the status of a button
33 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
34 pub enum ButtonState {
35     /// Button is being hovered over by pointer
36     Hovered,
37     /// Button is not being hovered over by pointer
38     Idle,
39     /// Button is disabled
40     Disabled,
41 }
42 
43 /// Represents the status of a window
44 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
45 pub enum WindowState {
46     /// The window is active, in the foreground
47     Active,
48     /// The window is inactive, in the background
49     Inactive,
50 }
51 
52 impl From<bool> for WindowState {
from(b: bool) -> WindowState53     fn from(b: bool) -> WindowState {
54         if b {
55             WindowState::Active
56         } else {
57             WindowState::Inactive
58         }
59     }
60 }
61 
62 impl From<WindowState> for bool {
from(s: WindowState) -> bool63     fn from(s: WindowState) -> bool {
64         match s {
65             WindowState::Active => true,
66             WindowState::Inactive => false,
67         }
68     }
69 }
70 
71 /// Possible events generated by a window that you need to handle
72 #[derive(Clone, Debug)]
73 pub enum Event {
74     /// The state of your window has been changed
75     Configure {
76         /// Optional new size for your *inner* surface
77         ///
78         /// This is the new size of the contents of your window
79         /// as suggested by the server. You can ignore it and choose
80         /// a new size if you want better control on the possible
81         /// sizes of your window.
82         ///
83         /// The size is expressed in logical pixels, you need to multiply it by
84         /// your buffer scale to get the actual number of pixels to draw.
85         ///
86         /// In all cases, these events can be generated in large batches
87         /// during an interactive resize, and you should buffer them before
88         /// processing them. You only need to handle the last one of a batch.
89         new_size: Option<(u32, u32)>,
90         /// New combination of states of your window
91         ///
92         /// Typically tells you if your surface is active/inactive, maximized,
93         /// etc...
94         states: Vec<State>,
95     },
96     /// A close request has been received
97     ///
98     /// Most likely the user has clicked on the close button of the decorations
99     /// or something equivalent
100     Close,
101     /// The decorations need to be refreshed
102     Refresh,
103 }
104 
105 /// Possible decoration modes for a Window
106 ///
107 /// This represents what your application requests from the server.
108 ///
109 /// In any case, the compositor may override your requests. In that case SCTK
110 /// will follow its decision.
111 ///
112 /// If you don't care about it, you should use `FollowServer` (which is the
113 /// SCTK default). It'd be the most ergonomic for your users.
114 #[derive(Debug, PartialEq, Eq)]
115 pub enum Decorations {
116     /// Request server-side decorations
117     ServerSide,
118     /// Force using the client-side `Frame`
119     ClientSide,
120     /// Follow the preference of the compositor
121     FollowServer,
122     /// Don't decorate the Window
123     None,
124 }
125 
126 struct WindowInner<F> {
127     frame: Rc<RefCell<F>>,
128     shell_surface: Arc<Box<dyn shell::ShellSurface>>,
129     user_impl: Box<dyn FnMut(Event, DispatchData)>,
130     min_size: (u32, u32),
131     max_size: Option<(u32, u32)>,
132     current_size: (u32, u32),
133     old_size: Option<(u32, u32)>,
134     decorated: bool,
135 }
136 
137 /// A window
138 ///
139 /// This wrapper handles for you the decoration of your window
140 /// and the interaction with the server regarding the shell protocol.
141 ///
142 /// You are still entirely responsible for drawing the contents of your
143 /// window.
144 ///
145 /// Note also that as the dimensions of wayland surfaces is defined by
146 /// their attached buffer, you need to keep the decorations in sync with
147 /// your contents via the `resize(..)` method.
148 ///
149 /// Different kind of decorations can be used by customizing the type
150 /// parameter. A few are provided in this crate if the `frames` cargo feature
151 /// is enabled, but any type implementing the `Frame` trait can do.
152 pub struct Window<F: Frame> {
153     frame: Rc<RefCell<F>>,
154     surface: wl_surface::WlSurface,
155     decoration: Option<ZxdgToplevelDecorationV1>,
156     shell_surface: Arc<Box<dyn shell::ShellSurface>>,
157     inner: Rc<RefCell<Option<WindowInner<F>>>>,
158     _seat_listener: crate::seat::SeatListener,
159 }
160 
161 impl<F: Frame + 'static> Window<F> {
162     /// Create a new window wrapping a given wayland surface as its main content and
163     /// following the compositor's preference regarding server-side decorations
164     ///
165     /// It can fail if the initialization of the frame fails (for example if the
166     /// frame class fails to initialize its SHM).
167     ///
168     /// Providing non `None` value for `theme_manager` should prevent theming pointer
169     /// over the `surface`.
init_with_decorations<Impl, E>( env: &crate::environment::Environment<E>, surface: wl_surface::WlSurface, theme_manager: Option<ThemeManager>, initial_dims: (u32, u32), implementation: Impl, ) -> Result<Window<F>, F::Error> where Impl: FnMut(Event, DispatchData) + 'static, E: GlobalHandler<wl_compositor::WlCompositor> + GlobalHandler<wl_subcompositor::WlSubcompositor> + GlobalHandler<wl_shm::WlShm> + crate::shell::ShellHandling + MultiGlobalHandler<wl_seat::WlSeat> + GlobalHandler<ZxdgDecorationManagerV1> + crate::seat::SeatHandling,170     fn init_with_decorations<Impl, E>(
171         env: &crate::environment::Environment<E>,
172         surface: wl_surface::WlSurface,
173         theme_manager: Option<ThemeManager>,
174         initial_dims: (u32, u32),
175         implementation: Impl,
176     ) -> Result<Window<F>, F::Error>
177     where
178         Impl: FnMut(Event, DispatchData) + 'static,
179         E: GlobalHandler<wl_compositor::WlCompositor>
180             + GlobalHandler<wl_subcompositor::WlSubcompositor>
181             + GlobalHandler<wl_shm::WlShm>
182             + crate::shell::ShellHandling
183             + MultiGlobalHandler<wl_seat::WlSeat>
184             + GlobalHandler<ZxdgDecorationManagerV1>
185             + crate::seat::SeatHandling,
186     {
187         let compositor = env.require_global::<wl_compositor::WlCompositor>();
188         let subcompositor = env.require_global::<wl_subcompositor::WlSubcompositor>();
189         let shm = env.require_global::<wl_shm::WlShm>();
190         let shell = env
191             .get_shell()
192             .expect("[SCTK] Cannot create a window if the compositor advertized no shell.");
193 
194         let inner = Rc::new(RefCell::new(None::<WindowInner<F>>));
195         let frame_inner = inner.clone();
196         let shell_inner = inner.clone();
197         let mut frame = F::init(
198             &surface,
199             &compositor,
200             &subcompositor,
201             &shm,
202             theme_manager,
203             Box::new(move |req, serial, ddata: DispatchData| {
204                 if let Some(ref mut inner) = *shell_inner.borrow_mut() {
205                     match req {
206                         FrameRequest::Minimize => inner.shell_surface.set_minimized(),
207                         FrameRequest::Maximize => inner.shell_surface.set_maximized(),
208                         FrameRequest::UnMaximize => inner.shell_surface.unset_maximized(),
209                         FrameRequest::Move(seat) => inner.shell_surface.move_(&seat, serial),
210                         FrameRequest::Resize(seat, edges) => {
211                             inner.shell_surface.resize(&seat, serial, edges)
212                         }
213                         FrameRequest::ShowMenu(seat, x, y) => {
214                             inner.shell_surface.show_window_menu(&seat, serial, x, y)
215                         }
216                         FrameRequest::Close => (inner.user_impl)(Event::Close, ddata),
217                         FrameRequest::Refresh => (inner.user_impl)(Event::Refresh, ddata),
218                     }
219                 }
220             }) as Box<_>,
221         )?;
222 
223         let decoration_mgr = env.get_global::<ZxdgDecorationManagerV1>();
224         if decoration_mgr.is_none() {
225             // We don't have ServerSide decorations, so we'll be using CSD, and so should
226             // mark frame as not hidden.
227             frame.set_hidden(false);
228         }
229 
230         frame.resize(initial_dims);
231         let frame = Rc::new(RefCell::new(frame));
232         let shell_surface = Arc::new(shell::create_shell_surface(
233             &shell,
234             &surface,
235             move |event, mut ddata: DispatchData| {
236                 let mut frame_inner = frame_inner.borrow_mut();
237                 let mut inner = match frame_inner.as_mut() {
238                     Some(inner) => inner,
239                     None => return,
240                 };
241 
242                 match event {
243                     shell::Event::Configure { states, mut new_size } => {
244                         let mut frame = inner.frame.borrow_mut();
245 
246                         // Populate frame changes. We should do it before performing new_size
247                         // recalculation, since we should account for a fullscreen state.
248                         let need_refresh = frame.set_states(&states);
249 
250                         // Clamp size.
251                         new_size = new_size.map(|(w, h)| {
252                             use std::cmp::{max, min};
253                             let (mut w, mut h) = frame.subtract_borders(w as i32, h as i32);
254                             let (minw, minh) = inner.min_size;
255                             w = max(w, minw as i32);
256                             h = max(h, minh as i32);
257                             if let Some((maxw, maxh)) = inner.max_size {
258                                 w = min(w, maxw as i32);
259                                 h = min(h, maxh as i32);
260                             }
261                             (max(w, 1) as u32, max(h, 1) as u32)
262                         });
263 
264                         // Check whether we should save old size for later restoration.
265                         let should_stash_size = states
266                             .iter()
267                             .find(|s| {
268                                 matches!(
269                                     *s,
270                                     State::Maximized
271                                         | State::Fullscreen
272                                         | State::TiledTop
273                                         | State::TiledRight
274                                         | State::TiledBottom
275                                         | State::TiledLeft
276                                 )
277                             })
278                             .map(|_| true)
279                             .unwrap_or(false);
280 
281                         if should_stash_size {
282                             if inner.old_size.is_none() {
283                                 // We are getting maximized/fullscreened, store the size for
284                                 // restoration.
285                                 inner.old_size = Some(inner.current_size);
286                             }
287                         } else if new_size.is_none() {
288                             // We are getting de-maximized/de-fullscreened/un-tiled, restore the
289                             // size, if we were not previously maximized/fullscreened, old_size is
290                             // None and this does nothing.
291                             new_size = inner.old_size.take();
292                         } else {
293                             // We are neither maximized nor fullscreened, but are given a size,
294                             // respect it and forget about the old size.
295                             inner.old_size = None;
296                         }
297 
298                         if need_refresh {
299                             (inner.user_impl)(Event::Refresh, ddata.reborrow());
300                         }
301                         (inner.user_impl)(Event::Configure { states, new_size }, ddata);
302                     }
303                     shell::Event::Close => {
304                         (inner.user_impl)(Event::Close, ddata);
305                     }
306                 }
307             },
308         ));
309 
310         // setup size and geometry
311         {
312             let frame = frame.borrow_mut();
313             let (minw, minh) =
314                 frame.add_borders(MIN_WINDOW_SIZE.0 as i32, MIN_WINDOW_SIZE.1 as i32);
315             shell_surface.set_min_size(Some((minw, minh)));
316             let (w, h) = frame.add_borders(initial_dims.0 as i32, initial_dims.1 as i32);
317             let (x, y) = frame.location();
318             shell_surface.set_geometry(x, y, w, h);
319         }
320 
321         // initial seat setup
322         let mut seats = Vec::<wl_seat::WlSeat>::new();
323         for seat in env.get_all_seats() {
324             crate::seat::with_seat_data(&seat, |seat_data| {
325                 if seat_data.has_pointer && !seat_data.defunct {
326                     seats.push(seat.detach());
327                     frame.borrow_mut().new_seat(&seat);
328                 }
329             });
330         }
331 
332         // setup seat_listener
333         let seat_frame = frame.clone();
334         let seat_listener = env.listen_for_seats(move |seat, seat_data, _| {
335             let is_known = seats.contains(&seat);
336             if !is_known && seat_data.has_pointer && !seat_data.defunct {
337                 seat_frame.borrow_mut().new_seat(&seat);
338                 seats.push(seat.detach());
339             } else if is_known && ((!seat_data.has_pointer) || seat_data.defunct) {
340                 seat_frame.borrow_mut().remove_seat(&seat);
341                 seats.retain(|s| s != &*seat);
342             }
343         });
344 
345         *inner.borrow_mut() = Some(WindowInner {
346             frame: frame.clone(),
347             shell_surface: shell_surface.clone(),
348             user_impl: Box::new(implementation) as Box<_>,
349             min_size: (MIN_WINDOW_SIZE.0, MIN_WINDOW_SIZE.1),
350             max_size: None,
351             current_size: initial_dims,
352             old_size: None,
353             decorated: true,
354         });
355 
356         // Setup window decorations if applicable.
357         let decoration = Self::setup_decorations_handler(
358             &decoration_mgr,
359             &shell_surface,
360             frame.clone(),
361             inner.clone(),
362         );
363 
364         let window = Window {
365             frame,
366             shell_surface,
367             decoration,
368             surface,
369             inner,
370             _seat_listener: seat_listener,
371         };
372 
373         Ok(window)
374     }
375 
376     /// Setup handling for zxdg_toplevel_decoration_v1 in case protocol is available.
setup_decorations_handler( decoration_mgr: &Option<Attached<ZxdgDecorationManagerV1>>, shell_surface: &Arc<Box<dyn shell::ShellSurface>>, decoration_frame: Rc<RefCell<F>>, decoration_inner: Rc<RefCell<Option<WindowInner<F>>>>, ) -> Option<ZxdgToplevelDecorationV1>377     fn setup_decorations_handler(
378         decoration_mgr: &Option<Attached<ZxdgDecorationManagerV1>>,
379         shell_surface: &Arc<Box<dyn shell::ShellSurface>>,
380         decoration_frame: Rc<RefCell<F>>,
381         decoration_inner: Rc<RefCell<Option<WindowInner<F>>>>,
382     ) -> Option<ZxdgToplevelDecorationV1> {
383         let (toplevel, mgr) = match (shell_surface.get_xdg(), decoration_mgr) {
384             (Some(toplevel), Some(ref mgr)) => (toplevel, mgr),
385             _ => {
386                 return None;
387             }
388         };
389 
390         let decoration = mgr.get_toplevel_decoration(toplevel);
391 
392         decoration.quick_assign(move |_, event, _| {
393             use self::zxdg_toplevel_decoration_v1::{Event, Mode};
394             let mode = if let Event::Configure { mode } = event { mode } else { unreachable!() };
395 
396             match mode {
397                 Mode::ServerSide => {
398                     decoration_frame.borrow_mut().set_hidden(true);
399                 }
400                 Mode::ClientSide => {
401                     let want_decorate = decoration_inner
402                         .borrow_mut()
403                         .as_ref()
404                         .map(|inner| inner.decorated)
405                         .unwrap_or(false);
406                     decoration_frame.borrow_mut().set_hidden(!want_decorate);
407                 }
408                 _ => unreachable!(),
409             }
410         });
411 
412         Some(decoration.detach())
413     }
414 
415     /// Access the surface wrapped in this Window
surface(&self) -> &wl_surface::WlSurface416     pub fn surface(&self) -> &wl_surface::WlSurface {
417         &self.surface
418     }
419 
420     /// Refreshes the frame
421     ///
422     /// Redraws the frame to match its requested state (dimensions, presence/
423     /// absence of decorations, ...)
424     ///
425     /// You need to call this method after every change to the dimensions or state
426     /// of the decorations of your window, otherwise the drawn decorations may go
427     /// out of sync with the state of your content.
428     ///
429     /// Your implementation will also receive `Refresh` events when the frame requests
430     /// to be redrawn (to provide some frame animations for example).
refresh(&mut self)431     pub fn refresh(&mut self) {
432         self.frame.borrow_mut().redraw();
433     }
434 
435     /// Set a short title for the window.
436     ///
437     /// This string may be used to identify the surface in a task bar, window list, or other
438     /// user interface elements provided by the compositor.
439     ///
440     /// You need to call `refresh()` afterwards for this to properly
441     /// take effect.
set_title(&self, mut title: String)442     pub fn set_title(&self, mut title: String) {
443         // Truncate the title to at most 1024 bytes, so that it does not blow up the protocol
444         // messages
445         if title.len() > 1024 {
446             let mut new_len = 1024;
447             while !title.is_char_boundary(new_len) {
448                 new_len -= 1;
449             }
450             title.truncate(new_len);
451         }
452         self.frame.borrow_mut().set_title(title.clone());
453         self.shell_surface.set_title(title);
454     }
455 
456     /// Set an app id for the surface.
457     ///
458     /// The surface class identifies the general class of applications to which the surface
459     /// belongs.
460     ///
461     /// Several wayland compositors will try to find a `.desktop` file matching this name
462     /// to find metadata about your apps.
set_app_id(&self, app_id: String)463     pub fn set_app_id(&self, app_id: String) {
464         self.shell_surface.set_app_id(app_id);
465     }
466 
467     /// Set whether the window should be decorated or not.
468     ///
469     /// If `zxdg_toplevel_decoration_v1` object is presented and alive, requesting `None`
470     /// decorations will result in setting `ClientSide` decorations with hidden frame, and if
471     /// `ClientSide` decorations were requested, it'll result in destroying
472     /// `zxdg_toplevel_decoration_v1` object, meaning that you won't be able to get `ServerSide`
473     /// decorations back.
474     ///
475     /// In case `zxdg_toplevel_decoration_v1` is not available or the corresponding object is not
476     /// alive anymore, `decorate` with `ServerSide` or `FollowServer` values will always result in
477     /// `ClientSide` decorations being used.
478     ///
479     /// You need to call `refresh()` afterwards for this to properly
480     /// take effect.
set_decorate(&mut self, decorate: Decorations)481     pub fn set_decorate(&mut self, decorate: Decorations) {
482         use self::zxdg_toplevel_decoration_v1::Mode;
483 
484         // Update inner.decorated state.
485         if let Some(inner) = self.inner.borrow_mut().as_mut() {
486             if Decorations::None == decorate {
487                 inner.decorated = false;
488             } else {
489                 inner.decorated = true;
490             }
491         }
492 
493         match self.decoration.as_ref() {
494             // Server side decorations are there.
495             Some(decoration) => {
496                 match decorate {
497                     Decorations::ClientSide => {
498                         // The user explicitly requested `ClientSide` decorations, we should destroy
499                         // the server side decorations if some are presented.
500                         decoration.destroy();
501                         self.decoration = None;
502                         self.frame.borrow_mut().set_hidden(false);
503                     }
504                     Decorations::ServerSide => {
505                         decoration.set_mode(Mode::ServerSide);
506                     }
507                     Decorations::FollowServer => {
508                         decoration.unset_mode();
509                     }
510                     Decorations::None => {
511                         // The user explicitly requested `None` decorations, however
512                         // since we can't destroy and recreate decoration object on the fly switch
513                         // them to `ClientSide` with the hidden frame. The server is free to ignore
514                         // us with such request, but not that we can do much about it.
515                         decoration.set_mode(Mode::ClientSide);
516                         self.frame.borrow_mut().set_hidden(true);
517                     }
518                 }
519             }
520             // Server side decorations are not presented or were destroyed.
521             None => {
522                 match decorate {
523                     // We map `ServerSide` and `FollowServer` decorations to `ClientSide`, since
524                     // server side decorations are no longer available.
525                     Decorations::ClientSide
526                     | Decorations::ServerSide
527                     | Decorations::FollowServer => {
528                         self.frame.borrow_mut().set_hidden(false);
529                     }
530                     Decorations::None => {
531                         self.frame.borrow_mut().set_hidden(true);
532                     }
533                 }
534             }
535         }
536     }
537 
538     /// Set whether the window should be resizeable by the user
539     ///
540     /// This is not an hard blocking, as the compositor can always
541     /// resize you forcibly if it wants. However it signals it that
542     /// you don't want this window to be resized.
543     ///
544     /// Additionally, the decorations will stop suggesting the user
545     /// to resize by dragging the borders if you set the window as
546     /// non-resizable.
547     ///
548     /// When re-activating resizability, any previously set min/max
549     /// sizes are restored.
set_resizable(&self, resizable: bool)550     pub fn set_resizable(&self, resizable: bool) {
551         let mut frame = self.frame.borrow_mut();
552         frame.set_resizable(resizable);
553         let mut inner = self.inner.borrow_mut();
554         if let Some(ref mut inner) = *inner {
555             if resizable {
556                 // restore the min/max sizes
557                 self.shell_surface.set_min_size(
558                     Some(inner.min_size).map(|(w, h)| frame.add_borders(w as i32, h as i32)),
559                 );
560                 self.shell_surface.set_max_size(
561                     inner.max_size.map(|(w, h)| frame.add_borders(w as i32, h as i32)),
562                 );
563             } else {
564                 // Lock the min/max sizes to current size.
565                 let (w, h) = inner.current_size;
566                 self.shell_surface.set_min_size(Some(frame.add_borders(w as i32, h as i32)));
567                 self.shell_surface.set_max_size(Some(frame.add_borders(w as i32, h as i32)));
568             }
569         }
570     }
571 
572     /// Resize the decorations
573     ///
574     /// You should call this whenever you change the size of the contents
575     /// of your window, with the new _inner size_ of your window.
576     ///
577     /// This size is expressed in logical pixels, like the one received
578     /// in [`Event::Configure`](enum.Event.html).
579     ///
580     /// You need to call `refresh()` afterwards for this to properly
581     /// take effect.
resize(&mut self, w: u32, h: u32)582     pub fn resize(&mut self, w: u32, h: u32) {
583         use std::cmp::max;
584         let w = max(w, 1);
585         let h = max(h, 1);
586         if let Some(ref mut inner) = *self.inner.borrow_mut() {
587             inner.current_size = (w, h);
588         }
589         let mut frame = self.frame.borrow_mut();
590         frame.resize((w, h));
591         let (w, h) = frame.add_borders(w as i32, h as i32);
592         let (x, y) = frame.location();
593         self.shell_surface.set_geometry(x, y, w, h);
594     }
595 
596     /// Request the window to be maximized
set_maximized(&self)597     pub fn set_maximized(&self) {
598         self.shell_surface.set_maximized();
599     }
600 
601     /// Request the window to be un-maximized
unset_maximized(&self)602     pub fn unset_maximized(&self) {
603         self.shell_surface.unset_maximized();
604     }
605 
606     /// Request the window to be minimized
set_minimized(&self)607     pub fn set_minimized(&self) {
608         self.shell_surface.set_minimized();
609     }
610 
611     /// Request the window to be set fullscreen
612     ///
613     /// Note: The decorations hiding behavior is `Frame` dependant.
614     /// To check whether you need to hide them consult your frame documentation.
set_fullscreen(&self, output: Option<&wl_output::WlOutput>)615     pub fn set_fullscreen(&self, output: Option<&wl_output::WlOutput>) {
616         self.shell_surface.set_fullscreen(output);
617     }
618 
619     /// Request the window to quit fullscreen mode
unset_fullscreen(&self)620     pub fn unset_fullscreen(&self) {
621         self.shell_surface.unset_fullscreen();
622     }
623 
624     /// Sets the minimum possible size for this window
625     ///
626     /// Provide either a tuple `Some((width, height))` or `None` to unset the
627     /// minimum size.
628     ///
629     /// Setting either value in the tuple to `0` means that this axis should not
630     /// be limited.
631     ///
632     /// The provided size is the interior size, not counting decorations.
633     ///
634     /// This size is expressed in logical pixels, like the one received
635     /// in [`Event::Configure`](enum.Event.html).
set_min_size(&mut self, size: Option<(u32, u32)>)636     pub fn set_min_size(&mut self, size: Option<(u32, u32)>) {
637         let (w, h) = size.unwrap_or(MIN_WINDOW_SIZE);
638         let (w, h) = self.frame.borrow_mut().add_borders(w as i32, h as i32);
639         self.shell_surface.set_min_size(Some((w, h)));
640         if let Some(ref mut inner) = *self.inner.borrow_mut() {
641             inner.min_size = size.unwrap_or(MIN_WINDOW_SIZE)
642         }
643     }
644 
645     /// Sets the maximum possible size for this window
646     ///
647     /// Provide either a tuple `Some((width, height))` or `None` to unset the
648     /// maximum size.
649     ///
650     /// Setting either value in the tuple to `0` means that this axis should not
651     /// be limited.
652     ///
653     /// The provided size is the interior size, not counting decorations.
654     ///
655     /// This size is expressed in logical pixels, like the one received
656     /// in [`Event::Configure`](enum.Event.html).
set_max_size(&mut self, size: Option<(u32, u32)>)657     pub fn set_max_size(&mut self, size: Option<(u32, u32)>) {
658         let max_size = size.map(|(w, h)| self.frame.borrow_mut().add_borders(w as i32, h as i32));
659         self.shell_surface.set_max_size(max_size);
660         if let Some(ref mut inner) = *self.inner.borrow_mut() {
661             inner.max_size = size.map(|(w, h)| (w as u32, h as u32));
662         }
663     }
664 
665     /// Sets the frame configuration for the window
666     ///
667     /// This allows to configure the frame at runtime if it supports
668     /// it. See the documentation of your `Frame` implementation for
669     /// details about what configuration it supports.
set_frame_config(&mut self, config: F::Config)670     pub fn set_frame_config(&mut self, config: F::Config) {
671         self.frame.borrow_mut().set_config(config)
672     }
673 
674     /// Start an interactive, user-driven move of the surface
675     ///
676     /// This request must be used in response to some sort of user action
677     /// like a button press, key press, or touch down event. The passed
678     /// serial is used to determine the type of interactive move (touch,
679     /// pointer, etc).
680     ///
681     /// The server may ignore move requests depending on the state of
682     /// the surface (e.g. fullscreen or maximized), or if the passed serial
683     /// is no longer valid.
start_interactive_move(&self, seat: &wl_seat::WlSeat, serial: u32)684     pub fn start_interactive_move(&self, seat: &wl_seat::WlSeat, serial: u32) {
685         self.shell_surface.move_(seat, serial);
686     }
687 }
688 
689 impl<F: Frame> Drop for Window<F> {
drop(&mut self)690     fn drop(&mut self) {
691         self.inner.borrow_mut().take();
692     }
693 }
694 
695 /// Request generated by a Frame
696 ///
697 /// These requests are generated by a Frame and the Window will
698 /// forward them appropriately to the server.
699 pub enum FrameRequest {
700     /// The window should be minimized
701     Minimize,
702     /// The window should be maximized
703     Maximize,
704     /// The window should be unmaximized
705     UnMaximize,
706     /// The window should be closed
707     Close,
708     /// An interactive move should be started
709     Move(wl_seat::WlSeat),
710     /// An interactive resize should be started
711     Resize(wl_seat::WlSeat, ResizeEdge),
712     /// Show window menu.
713     ShowMenu(wl_seat::WlSeat, i32, i32),
714     /// The frame requests to be refreshed
715     Refresh,
716 }
717 
718 /// Interface for defining the drawing of decorations
719 ///
720 /// A type implementing this trait can be used to define custom
721 /// decorations additionnaly to the ones provided by this crate
722 /// and be used with `Window`.
723 pub trait Frame: Sized {
724     /// Type of errors that may occur when attempting to create a frame
725     type Error;
726     /// Configuration for this frame
727     type Config;
728     /// Initialize the Frame.
729     ///
730     /// Providing non `None` to `theme_manager` should prevent `Frame` to theme pointer
731     /// over `base_surface` surface.
init( base_surface: &wl_surface::WlSurface, compositor: &Attached<wl_compositor::WlCompositor>, subcompositor: &Attached<wl_subcompositor::WlSubcompositor>, shm: &Attached<wl_shm::WlShm>, theme_manager: Option<ThemeManager>, callback: Box<dyn FnMut(FrameRequest, u32, DispatchData)>, ) -> Result<Self, Self::Error>732     fn init(
733         base_surface: &wl_surface::WlSurface,
734         compositor: &Attached<wl_compositor::WlCompositor>,
735         subcompositor: &Attached<wl_subcompositor::WlSubcompositor>,
736         shm: &Attached<wl_shm::WlShm>,
737         theme_manager: Option<ThemeManager>,
738         callback: Box<dyn FnMut(FrameRequest, u32, DispatchData)>,
739     ) -> Result<Self, Self::Error>;
740     /// Set the Window XDG states for the frame
741     ///
742     /// This notably includes information about whether the window is
743     /// maximized, active, or tiled, and can affect the way decorations
744     /// are drawn.
745     ///
746     /// Calling this should *not* trigger a redraw, but return `true` if
747     /// a redraw is needed.
set_states(&mut self, states: &[State]) -> bool748     fn set_states(&mut self, states: &[State]) -> bool;
749     /// Hide or show the decorations
750     ///
751     /// Calling this should *not* trigger a redraw
set_hidden(&mut self, hidden: bool)752     fn set_hidden(&mut self, hidden: bool);
753     /// Set whether interactive resize hints should be displayed
754     /// and reacted to
set_resizable(&mut self, resizable: bool)755     fn set_resizable(&mut self, resizable: bool);
756     /// Notify that a new wl_seat should be handled
757     ///
758     /// This seat is guaranteed to have pointer capability
new_seat(&mut self, seat: &Attached<wl_seat::WlSeat>)759     fn new_seat(&mut self, seat: &Attached<wl_seat::WlSeat>);
760     /// Notify that this seat has lost the pointer capability or
761     /// has been lost
remove_seat(&mut self, seat: &wl_seat::WlSeat)762     fn remove_seat(&mut self, seat: &wl_seat::WlSeat);
763     /// Change the size of the decorations
764     ///
765     /// Calling this should *not* trigger a redraw
resize(&mut self, newsize: (u32, u32))766     fn resize(&mut self, newsize: (u32, u32));
767     /// Redraw the decorations
redraw(&mut self)768     fn redraw(&mut self);
769     /// Subtracts the border dimensions from the given dimensions.
subtract_borders(&self, width: i32, height: i32) -> (i32, i32)770     fn subtract_borders(&self, width: i32, height: i32) -> (i32, i32);
771     /// Adds the border dimensions to the given dimensions.
add_borders(&self, width: i32, height: i32) -> (i32, i32)772     fn add_borders(&self, width: i32, height: i32) -> (i32, i32);
773     /// Returns the coordinates of the top-left corner of the borders relative to the content
774     ///
775     /// Values should thus be negative
location(&self) -> (i32, i32)776     fn location(&self) -> (i32, i32) {
777         (0, 0)
778     }
779     /// Sets the configuration for the frame
set_config(&mut self, config: Self::Config)780     fn set_config(&mut self, config: Self::Config);
781 
782     /// Sets the frames title
set_title(&mut self, title: String)783     fn set_title(&mut self, title: String);
784 }
785 
786 impl<E> Environment<E>
787 where
788     E: GlobalHandler<wl_compositor::WlCompositor>
789         + GlobalHandler<wl_subcompositor::WlSubcompositor>
790         + GlobalHandler<wl_shm::WlShm>
791         + crate::shell::ShellHandling
792         + MultiGlobalHandler<wl_seat::WlSeat>
793         + GlobalHandler<ZxdgDecorationManagerV1>
794         + crate::seat::SeatHandling,
795 {
796     /// Create a new window wrapping given surface
797     ///
798     /// This window handles decorations for you, this includes
799     /// drawing them if the compositor doe snot support them, resizing interactions
800     /// and moving the window. It also provides close/maximize/minimize buttons.
801     ///
802     /// Many interactions still require your input, and are given to you via the
803     /// callback you need to provide.
create_window<F: Frame + 'static, CB>( &self, surface: wl_surface::WlSurface, theme_manager: Option<ThemeManager>, initial_dims: (u32, u32), callback: CB, ) -> Result<Window<F>, F::Error> where CB: FnMut(Event, DispatchData) + 'static,804     pub fn create_window<F: Frame + 'static, CB>(
805         &self,
806         surface: wl_surface::WlSurface,
807         theme_manager: Option<ThemeManager>,
808         initial_dims: (u32, u32),
809         callback: CB,
810     ) -> Result<Window<F>, F::Error>
811     where
812         CB: FnMut(Event, DispatchData) + 'static,
813     {
814         Window::<F>::init_with_decorations(self, surface, theme_manager, initial_dims, callback)
815     }
816 }
817