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