oden/third-party/vendor/smithay-client-toolkit/src/window/mod.rs
2024-03-08 11:03:01 -08:00

857 lines
32 KiB
Rust

//! Window abstraction
use std::cell::RefCell;
use std::fmt;
use std::rc::Rc;
use std::sync::Arc;
use wayland_client::protocol::{
wl_compositor, wl_output, wl_seat, wl_shm, wl_subcompositor, wl_surface,
};
use wayland_client::{Attached, DispatchData};
use wayland_protocols::xdg_shell::client::xdg_toplevel::ResizeEdge;
pub use wayland_protocols::xdg_shell::client::xdg_toplevel::State;
use wayland_protocols::unstable::xdg_decoration::v1::client::{
zxdg_decoration_manager_v1::ZxdgDecorationManagerV1,
zxdg_toplevel_decoration_v1::{self, ZxdgToplevelDecorationV1},
};
use crate::{
environment::{Environment, GlobalHandler, MultiGlobalHandler},
seat::pointer::ThemeManager,
shell,
};
mod fallback_frame;
pub use self::fallback_frame::FallbackFrame;
// Defines the minimum window size. Minimum width is set to 2 pixels to circumvent
// a bug in mutter - https://gitlab.gnome.org/GNOME/mutter/issues/259
const MIN_WINDOW_SIZE: (u32, u32) = (2, 1);
/// Represents the status of a button
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ButtonState {
/// Button is being hovered over by pointer
Hovered,
/// Button is not being hovered over by pointer
Idle,
/// Button is disabled
Disabled,
}
/// Represents the status of a window
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum WindowState {
/// The window is active, in the foreground
Active,
/// The window is inactive, in the background
Inactive,
}
impl From<bool> for WindowState {
fn from(b: bool) -> WindowState {
if b {
WindowState::Active
} else {
WindowState::Inactive
}
}
}
impl From<WindowState> for bool {
fn from(s: WindowState) -> bool {
match s {
WindowState::Active => true,
WindowState::Inactive => false,
}
}
}
/// Possible events generated by a window that you need to handle
#[derive(Clone, Debug)]
pub enum Event {
/// The state of your window has been changed
Configure {
/// Optional new size for your *inner* surface
///
/// This is the new size of the contents of your window
/// as suggested by the server. You can ignore it and choose
/// a new size if you want better control on the possible
/// sizes of your window.
///
/// The size is expressed in logical pixels, you need to multiply it by
/// your buffer scale to get the actual number of pixels to draw.
///
/// In all cases, these events can be generated in large batches
/// during an interactive resize, and you should buffer them before
/// processing them. You only need to handle the last one of a batch.
new_size: Option<(u32, u32)>,
/// New combination of states of your window
///
/// Typically tells you if your surface is active/inactive, maximized,
/// etc...
states: Vec<State>,
},
/// A close request has been received
///
/// Most likely the user has clicked on the close button of the decorations
/// or something equivalent
Close,
/// The decorations need to be refreshed
Refresh,
}
/// Possible decoration modes for a Window
///
/// This represents what your application requests from the server.
///
/// In any case, the compositor may override your requests. In that case SCTK
/// will follow its decision.
///
/// If you don't care about it, you should use `FollowServer` (which is the
/// SCTK default). It'd be the most ergonomic for your users.
#[derive(Debug, PartialEq, Eq)]
pub enum Decorations {
/// Request server-side decorations
ServerSide,
/// Force using the client-side `Frame`
ClientSide,
/// Follow the preference of the compositor
FollowServer,
/// Don't decorate the Window
None,
}
struct WindowInner<F> {
frame: Rc<RefCell<F>>,
shell_surface: Arc<Box<dyn shell::ShellSurface>>,
user_impl: Box<dyn FnMut(Event, DispatchData)>,
min_size: (u32, u32),
max_size: Option<(u32, u32)>,
current_size: (u32, u32),
old_size: Option<(u32, u32)>,
decorated: bool,
}
impl<F> fmt::Debug for WindowInner<F>
where
F: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WindowInner")
.field("frame", &self.frame)
.field("shell_surface", &self.shell_surface)
.field("user_impl", &"Fn() -> { ... }")
.field("min_size", &self.min_size)
.field("max_size", &self.max_size)
.field("current_size", &self.current_size)
.field("old_size", &self.old_size)
.field("decorated", &self.decorated)
.finish()
}
}
/// A window
///
/// This wrapper handles for you the decoration of your window
/// and the interaction with the server regarding the shell protocol.
///
/// You are still entirely responsible for drawing the contents of your
/// window.
///
/// Note also that as the dimensions of wayland surfaces is defined by
/// their attached buffer, you need to keep the decorations in sync with
/// your contents via the `resize(..)` method.
///
/// Different kind of decorations can be used by customizing the type
/// parameter. A few are provided in this crate if the `frames` cargo feature
/// is enabled, but any type implementing the `Frame` trait can do.
pub struct Window<F: Frame> {
frame: Rc<RefCell<F>>,
surface: wl_surface::WlSurface,
decoration: Option<ZxdgToplevelDecorationV1>,
shell_surface: Arc<Box<dyn shell::ShellSurface>>,
inner: Rc<RefCell<Option<WindowInner<F>>>>,
_seat_listener: crate::seat::SeatListener,
}
impl<F: Frame + 'static> Window<F> {
/// Create a new window wrapping a given wayland surface as its main content and
/// following the compositor's preference regarding server-side decorations
///
/// It can fail if the initialization of the frame fails (for example if the
/// frame class fails to initialize its SHM).
///
/// Providing non `None` value for `theme_manager` should prevent theming pointer
/// over the `surface`.
fn 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,
{
let compositor = env.require_global::<wl_compositor::WlCompositor>();
let subcompositor = env.require_global::<wl_subcompositor::WlSubcompositor>();
let shm = env.require_global::<wl_shm::WlShm>();
let shell = env
.get_shell()
.expect("[SCTK] Cannot create a window if the compositor advertized no shell.");
let inner = Rc::new(RefCell::new(None::<WindowInner<F>>));
let frame_inner = inner.clone();
let shell_inner = inner.clone();
let mut frame = F::init(
&surface,
&compositor,
&subcompositor,
&shm,
theme_manager,
Box::new(move |req, serial, ddata: DispatchData| {
if let Some(ref mut inner) = *shell_inner.borrow_mut() {
match req {
FrameRequest::Minimize => inner.shell_surface.set_minimized(),
FrameRequest::Maximize => inner.shell_surface.set_maximized(),
FrameRequest::UnMaximize => inner.shell_surface.unset_maximized(),
FrameRequest::Move(seat) => inner.shell_surface.move_(&seat, serial),
FrameRequest::Resize(seat, edges) => {
inner.shell_surface.resize(&seat, serial, edges)
}
FrameRequest::ShowMenu(seat, x, y) => {
inner.shell_surface.show_window_menu(&seat, serial, x, y)
}
FrameRequest::Close => (inner.user_impl)(Event::Close, ddata),
FrameRequest::Refresh => (inner.user_impl)(Event::Refresh, ddata),
}
}
}) as Box<_>,
)?;
let decoration_mgr = env.get_global::<ZxdgDecorationManagerV1>();
if decoration_mgr.is_none() {
// We don't have ServerSide decorations, so we'll be using CSD, and so should
// mark frame as not hidden.
frame.set_hidden(false);
}
frame.resize(initial_dims);
let frame = Rc::new(RefCell::new(frame));
let shell_surface = Arc::new(shell::create_shell_surface(
&shell,
&surface,
move |event, mut ddata: DispatchData| {
let mut frame_inner = frame_inner.borrow_mut();
let mut inner = match frame_inner.as_mut() {
Some(inner) => inner,
None => return,
};
match event {
shell::Event::Configure { states, mut new_size } => {
let mut frame = inner.frame.borrow_mut();
// Populate frame changes. We should do it before performing new_size
// recalculation, since we should account for a fullscreen state.
let need_refresh = frame.set_states(&states);
// Clamp size.
new_size = new_size.map(|(w, h)| {
use std::cmp::{max, min};
let (mut w, mut h) = frame.subtract_borders(w as i32, h as i32);
let (minw, minh) = inner.min_size;
w = max(w, minw as i32);
h = max(h, minh as i32);
if let Some((maxw, maxh)) = inner.max_size {
w = min(w, maxw as i32);
h = min(h, maxh as i32);
}
(max(w, 1) as u32, max(h, 1) as u32)
});
// Check whether we should save old size for later restoration.
let should_stash_size = states
.iter()
.find(|s| {
matches!(
*s,
State::Maximized
| State::Fullscreen
| State::TiledTop
| State::TiledRight
| State::TiledBottom
| State::TiledLeft
)
})
.map(|_| true)
.unwrap_or(false);
if should_stash_size {
if inner.old_size.is_none() {
// We are getting maximized/fullscreened, store the size for
// restoration.
inner.old_size = Some(inner.current_size);
}
} else if new_size.is_none() {
// We are getting de-maximized/de-fullscreened/un-tiled, restore the
// size, if we were not previously maximized/fullscreened, old_size is
// None and this does nothing.
new_size = inner.old_size.take();
} else {
// We are neither maximized nor fullscreened, but are given a size,
// respect it and forget about the old size.
inner.old_size = None;
}
if need_refresh {
(inner.user_impl)(Event::Refresh, ddata.reborrow());
}
(inner.user_impl)(Event::Configure { states, new_size }, ddata);
}
shell::Event::Close => {
(inner.user_impl)(Event::Close, ddata);
}
}
},
));
// setup size and geometry
{
let frame = frame.borrow_mut();
let (minw, minh) =
frame.add_borders(MIN_WINDOW_SIZE.0 as i32, MIN_WINDOW_SIZE.1 as i32);
shell_surface.set_min_size(Some((minw, minh)));
let (w, h) = frame.add_borders(initial_dims.0 as i32, initial_dims.1 as i32);
let (x, y) = frame.location();
shell_surface.set_geometry(x, y, w, h);
}
// initial seat setup
let mut seats = Vec::<wl_seat::WlSeat>::new();
for seat in env.get_all_seats() {
crate::seat::with_seat_data(&seat, |seat_data| {
if seat_data.has_pointer && !seat_data.defunct {
seats.push(seat.detach());
frame.borrow_mut().new_seat(&seat);
}
});
}
// setup seat_listener
let seat_frame = frame.clone();
let seat_listener = env.listen_for_seats(move |seat, seat_data, _| {
let is_known = seats.contains(&seat);
if !is_known && seat_data.has_pointer && !seat_data.defunct {
seat_frame.borrow_mut().new_seat(&seat);
seats.push(seat.detach());
} else if is_known && ((!seat_data.has_pointer) || seat_data.defunct) {
seat_frame.borrow_mut().remove_seat(&seat);
seats.retain(|s| s != &*seat);
}
});
*inner.borrow_mut() = Some(WindowInner {
frame: frame.clone(),
shell_surface: shell_surface.clone(),
user_impl: Box::new(implementation) as Box<_>,
min_size: (MIN_WINDOW_SIZE.0, MIN_WINDOW_SIZE.1),
max_size: None,
current_size: initial_dims,
old_size: None,
decorated: true,
});
// Setup window decorations if applicable.
let decoration = Self::setup_decorations_handler(
&decoration_mgr,
&shell_surface,
frame.clone(),
inner.clone(),
);
let window = Window {
frame,
shell_surface,
decoration,
surface,
inner,
_seat_listener: seat_listener,
};
Ok(window)
}
/// Setup handling for zxdg_toplevel_decoration_v1 in case protocol is available.
fn 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> {
let (toplevel, mgr) = match (shell_surface.get_xdg(), decoration_mgr) {
(Some(toplevel), Some(ref mgr)) => (toplevel, mgr),
_ => {
return None;
}
};
let decoration = mgr.get_toplevel_decoration(toplevel);
decoration.quick_assign(move |_, event, _| {
use self::zxdg_toplevel_decoration_v1::{Event, Mode};
let mode = if let Event::Configure { mode } = event { mode } else { unreachable!() };
match mode {
Mode::ServerSide => {
decoration_frame.borrow_mut().set_hidden(true);
}
Mode::ClientSide => {
let want_decorate = decoration_inner
.borrow_mut()
.as_ref()
.map(|inner| inner.decorated)
.unwrap_or(false);
decoration_frame.borrow_mut().set_hidden(!want_decorate);
}
_ => unreachable!(),
}
});
Some(decoration.detach())
}
/// Access the surface wrapped in this Window
pub fn surface(&self) -> &wl_surface::WlSurface {
&self.surface
}
/// Refreshes the frame
///
/// Redraws the frame to match its requested state (dimensions, presence/
/// absence of decorations, ...)
///
/// You need to call this method after every change to the dimensions or state
/// of the decorations of your window, otherwise the drawn decorations may go
/// out of sync with the state of your content.
///
/// Your implementation will also receive `Refresh` events when the frame requests
/// to be redrawn (to provide some frame animations for example).
pub fn refresh(&mut self) {
self.frame.borrow_mut().redraw();
}
/// Set a short title for the window.
///
/// This string may be used to identify the surface in a task bar, window list, or other
/// user interface elements provided by the compositor.
///
/// You need to call `refresh()` afterwards for this to properly
/// take effect.
pub fn set_title(&self, mut title: String) {
// Truncate the title to at most 1024 bytes, so that it does not blow up the protocol
// messages
if title.len() > 1024 {
let mut new_len = 1024;
while !title.is_char_boundary(new_len) {
new_len -= 1;
}
title.truncate(new_len);
}
self.frame.borrow_mut().set_title(title.clone());
self.shell_surface.set_title(title);
}
/// Set an app id for the surface.
///
/// The surface class identifies the general class of applications to which the surface
/// belongs.
///
/// Several wayland compositors will try to find a `.desktop` file matching this name
/// to find metadata about your apps.
pub fn set_app_id(&self, app_id: String) {
self.shell_surface.set_app_id(app_id);
}
/// Set whether the window should be decorated or not.
///
/// If `zxdg_toplevel_decoration_v1` object is presented and alive, requesting `None`
/// decorations will result in setting `ClientSide` decorations with hidden frame, and if
/// `ClientSide` decorations were requested, it'll result in destroying
/// `zxdg_toplevel_decoration_v1` object, meaning that you won't be able to get `ServerSide`
/// decorations back.
///
/// In case `zxdg_toplevel_decoration_v1` is not available or the corresponding object is not
/// alive anymore, `decorate` with `ServerSide` or `FollowServer` values will always result in
/// `ClientSide` decorations being used.
///
/// You need to call `refresh()` afterwards for this to properly
/// take effect.
pub fn set_decorate(&mut self, decorate: Decorations) {
use self::zxdg_toplevel_decoration_v1::Mode;
// Update inner.decorated state.
if let Some(inner) = self.inner.borrow_mut().as_mut() {
if Decorations::None == decorate {
inner.decorated = false;
} else {
inner.decorated = true;
}
}
match self.decoration.as_ref() {
// Server side decorations are there.
Some(decoration) => {
match decorate {
Decorations::ClientSide => {
// The user explicitly requested `ClientSide` decorations, we should destroy
// the server side decorations if some are presented.
decoration.destroy();
self.decoration = None;
self.frame.borrow_mut().set_hidden(false);
}
Decorations::ServerSide => {
decoration.set_mode(Mode::ServerSide);
}
Decorations::FollowServer => {
decoration.unset_mode();
}
Decorations::None => {
// The user explicitly requested `None` decorations, however
// since we can't destroy and recreate decoration object on the fly switch
// them to `ClientSide` with the hidden frame. The server is free to ignore
// us with such request, but not that we can do much about it.
decoration.set_mode(Mode::ClientSide);
self.frame.borrow_mut().set_hidden(true);
}
}
}
// Server side decorations are not presented or were destroyed.
None => {
match decorate {
// We map `ServerSide` and `FollowServer` decorations to `ClientSide`, since
// server side decorations are no longer available.
Decorations::ClientSide
| Decorations::ServerSide
| Decorations::FollowServer => {
self.frame.borrow_mut().set_hidden(false);
}
Decorations::None => {
self.frame.borrow_mut().set_hidden(true);
}
}
}
}
}
/// Set whether the window should be resizeable by the user
///
/// This is not an hard blocking, as the compositor can always
/// resize you forcibly if it wants. However it signals it that
/// you don't want this window to be resized.
///
/// Additionally, the decorations will stop suggesting the user
/// to resize by dragging the borders if you set the window as
/// non-resizable.
///
/// When re-activating resizability, any previously set min/max
/// sizes are restored.
pub fn set_resizable(&self, resizable: bool) {
let mut frame = self.frame.borrow_mut();
frame.set_resizable(resizable);
let mut inner = self.inner.borrow_mut();
if let Some(ref mut inner) = *inner {
if resizable {
// restore the min/max sizes
self.shell_surface.set_min_size(
Some(inner.min_size).map(|(w, h)| frame.add_borders(w as i32, h as i32)),
);
self.shell_surface.set_max_size(
inner.max_size.map(|(w, h)| frame.add_borders(w as i32, h as i32)),
);
} else {
// Lock the min/max sizes to current size.
let (w, h) = inner.current_size;
self.shell_surface.set_min_size(Some(frame.add_borders(w as i32, h as i32)));
self.shell_surface.set_max_size(Some(frame.add_borders(w as i32, h as i32)));
}
}
}
/// Resize the decorations
///
/// You should call this whenever you change the size of the contents
/// of your window, with the new _inner size_ of your window.
///
/// This size is expressed in logical pixels, like the one received
/// in [`Event::Configure`](enum.Event.html).
///
/// You need to call `refresh()` afterwards for this to properly
/// take effect.
pub fn resize(&mut self, w: u32, h: u32) {
use std::cmp::max;
let w = max(w, 1);
let h = max(h, 1);
if let Some(ref mut inner) = *self.inner.borrow_mut() {
inner.current_size = (w, h);
}
let mut frame = self.frame.borrow_mut();
frame.resize((w, h));
let (w, h) = frame.add_borders(w as i32, h as i32);
let (x, y) = frame.location();
self.shell_surface.set_geometry(x, y, w, h);
}
/// Request the window to be maximized
pub fn set_maximized(&self) {
self.shell_surface.set_maximized();
}
/// Request the window to be un-maximized
pub fn unset_maximized(&self) {
self.shell_surface.unset_maximized();
}
/// Request the window to be minimized
pub fn set_minimized(&self) {
self.shell_surface.set_minimized();
}
/// Request the window to be set fullscreen
///
/// Note: The decorations hiding behavior is `Frame` dependant.
/// To check whether you need to hide them consult your frame documentation.
pub fn set_fullscreen(&self, output: Option<&wl_output::WlOutput>) {
self.shell_surface.set_fullscreen(output);
}
/// Request the window to quit fullscreen mode
pub fn unset_fullscreen(&self) {
self.shell_surface.unset_fullscreen();
}
/// Sets the minimum possible size for this window
///
/// Provide either a tuple `Some((width, height))` or `None` to unset the
/// minimum size.
///
/// Setting either value in the tuple to `0` means that this axis should not
/// be limited.
///
/// The provided size is the interior size, not counting decorations.
///
/// This size is expressed in logical pixels, like the one received
/// in [`Event::Configure`](enum.Event.html).
pub fn set_min_size(&mut self, size: Option<(u32, u32)>) {
let (w, h) = size.unwrap_or(MIN_WINDOW_SIZE);
let (w, h) = self.frame.borrow_mut().add_borders(w as i32, h as i32);
self.shell_surface.set_min_size(Some((w, h)));
if let Some(ref mut inner) = *self.inner.borrow_mut() {
inner.min_size = size.unwrap_or(MIN_WINDOW_SIZE)
}
}
/// Sets the maximum possible size for this window
///
/// Provide either a tuple `Some((width, height))` or `None` to unset the
/// maximum size.
///
/// Setting either value in the tuple to `0` means that this axis should not
/// be limited.
///
/// The provided size is the interior size, not counting decorations.
///
/// This size is expressed in logical pixels, like the one received
/// in [`Event::Configure`](enum.Event.html).
pub fn set_max_size(&mut self, size: Option<(u32, u32)>) {
let max_size = size.map(|(w, h)| self.frame.borrow_mut().add_borders(w as i32, h as i32));
self.shell_surface.set_max_size(max_size);
if let Some(ref mut inner) = *self.inner.borrow_mut() {
inner.max_size = size.map(|(w, h)| (w as u32, h as u32));
}
}
/// Sets the frame configuration for the window
///
/// This allows to configure the frame at runtime if it supports
/// it. See the documentation of your `Frame` implementation for
/// details about what configuration it supports.
pub fn set_frame_config(&mut self, config: F::Config) {
self.frame.borrow_mut().set_config(config)
}
/// Start an interactive, user-driven move of the surface
///
/// This request must be used in response to some sort of user action
/// like a button press, key press, or touch down event. The passed
/// serial is used to determine the type of interactive move (touch,
/// pointer, etc).
///
/// The server may ignore move requests depending on the state of
/// the surface (e.g. fullscreen or maximized), or if the passed serial
/// is no longer valid.
pub fn start_interactive_move(&self, seat: &wl_seat::WlSeat, serial: u32) {
self.shell_surface.move_(seat, serial);
}
}
impl<F: Frame> Drop for Window<F> {
fn drop(&mut self) {
self.inner.borrow_mut().take();
// Destroy decorations manager, so the inner frame could be dropped.
if let Some(decoration) = self.decoration.take() {
decoration.destroy();
}
}
}
impl<F: Frame> fmt::Debug for Window<F>
where
F: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Window")
.field("frame", &self.frame)
.field("surface", &self.surface)
.field("decoration", &self.decoration)
.field("shell_surface", &self.shell_surface)
.field("inner", &self.inner)
.field("_seat_listener", &self._seat_listener)
.finish()
}
}
/// Request generated by a Frame
///
/// These requests are generated by a Frame and the Window will
/// forward them appropriately to the server.
#[derive(Debug)]
pub enum FrameRequest {
/// The window should be minimized
Minimize,
/// The window should be maximized
Maximize,
/// The window should be unmaximized
UnMaximize,
/// The window should be closed
Close,
/// An interactive move should be started
Move(wl_seat::WlSeat),
/// An interactive resize should be started
Resize(wl_seat::WlSeat, ResizeEdge),
/// Show window menu.
ShowMenu(wl_seat::WlSeat, i32, i32),
/// The frame requests to be refreshed
Refresh,
}
/// Interface for defining the drawing of decorations
///
/// A type implementing this trait can be used to define custom
/// decorations additionnaly to the ones provided by this crate
/// and be used with `Window`.
pub trait Frame: Sized {
/// Type of errors that may occur when attempting to create a frame
type Error;
/// Configuration for this frame
type Config;
/// Initialize the Frame.
///
/// Providing non `None` to `theme_manager` should prevent `Frame` to theme pointer
/// over `base_surface` surface.
fn 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>;
/// Set the Window XDG states for the frame
///
/// This notably includes information about whether the window is
/// maximized, active, or tiled, and can affect the way decorations
/// are drawn.
///
/// Calling this should *not* trigger a redraw, but return `true` if
/// a redraw is needed.
fn set_states(&mut self, states: &[State]) -> bool;
/// Hide or show the decorations
///
/// Calling this should *not* trigger a redraw
fn set_hidden(&mut self, hidden: bool);
/// Set whether interactive resize hints should be displayed
/// and reacted to
fn set_resizable(&mut self, resizable: bool);
/// Notify that a new wl_seat should be handled
///
/// This seat is guaranteed to have pointer capability
fn new_seat(&mut self, seat: &Attached<wl_seat::WlSeat>);
/// Notify that this seat has lost the pointer capability or
/// has been lost
fn remove_seat(&mut self, seat: &wl_seat::WlSeat);
/// Change the size of the decorations
///
/// Calling this should *not* trigger a redraw
fn resize(&mut self, newsize: (u32, u32));
/// Redraw the decorations
fn redraw(&mut self);
/// Subtracts the border dimensions from the given dimensions.
fn subtract_borders(&self, width: i32, height: i32) -> (i32, i32);
/// Adds the border dimensions to the given dimensions.
fn add_borders(&self, width: i32, height: i32) -> (i32, i32);
/// Returns the coordinates of the top-left corner of the borders relative to the content
///
/// Values should thus be negative
fn location(&self) -> (i32, i32) {
(0, 0)
}
/// Sets the configuration for the frame
fn set_config(&mut self, config: Self::Config);
/// Sets the frames title
fn set_title(&mut self, title: String);
}
impl<E> Environment<E>
where
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,
{
/// Create a new window wrapping given surface
///
/// This window handles decorations for you, this includes
/// drawing them if the compositor doe snot support them, resizing interactions
/// and moving the window. It also provides close/maximize/minimize buttons.
///
/// Many interactions still require your input, and are given to you via the
/// callback you need to provide.
pub fn 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,
{
Window::<F>::init_with_decorations(self, surface, theme_manager, initial_dims, callback)
}
}