use std::sync::{Arc, Mutex};
use wayland_client::protocol::{
wl_compositor, wl_output, wl_seat, wl_shm, wl_subcompositor, wl_surface,
};
use wayland_client::Proxy;
use wayland_protocols::xdg_shell::client::xdg_toplevel::ResizeEdge;
pub use wayland_protocols::xdg_shell::client::xdg_toplevel::State;
use self::zxdg_decoration_manager_v1::RequestsTrait as DecorationMgrRequests;
use self::zxdg_toplevel_decoration_v1::RequestsTrait as DecorationRequests;
use wayland_protocols::unstable::xdg_decoration::v1::client::{
zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,
};
use {Environment, Shell};
mod basic_frame;
mod concept_frame;
use shell;
pub use self::basic_frame::BasicFrame;
pub use self::concept_frame::ConceptFrame;
const MIN_WINDOW_SIZE: (u32, u32) = (2, 1);
pub enum ButtonState {
Hovered,
Idle,
Disabled,
}
pub trait Theme: Send + 'static {
fn get_primary_color(&self, active: bool) -> [u8; 4];
fn get_secondary_color(&self, active: bool) -> [u8; 4];
fn get_close_button_color(&self, status: ButtonState) -> [u8; 4];
fn get_maximize_button_color(&self, status: ButtonState) -> [u8; 4];
fn get_minimize_button_color(&self, status: ButtonState) -> [u8; 4];
}
#[derive(Clone, Debug)]
pub enum Event {
Configure {
new_size: Option<(u32, u32)>,
states: Vec<State>,
},
Close,
Refresh,
}
struct WindowInner<F> {
frame: Arc<Mutex<F>>,
shell_surface: Arc<Box<shell::ShellSurface>>,
user_impl: Box<FnMut(Event) + Send>,
min_size: (u32, u32),
max_size: Option<(u32, u32)>,
current_size: (u32, u32),
old_size: Option<(u32, u32)>,
decorated: bool,
}
pub struct Window<F: Frame> {
frame: Arc<Mutex<F>>,
surface: Proxy<wl_surface::WlSurface>,
decoration: Mutex<Option<Proxy<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>>>,
decoration_mgr: Option<Proxy<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>>,
shell_surface: Arc<Box<shell::ShellSurface>>,
inner: Arc<Mutex<Option<WindowInner<F>>>>,
}
impl<F: Frame + 'static> Window<F> {
pub fn init_from_env<Impl>(
env: &Environment,
surface: Proxy<wl_surface::WlSurface>,
initial_dims: (u32, u32),
implementation: Impl,
) -> Result<Window<F>, F::Error>
where
Impl: FnMut(Event) + Send + 'static,
{
Self::init_with_decorations(
surface,
initial_dims,
&env.compositor,
&env.subcompositor,
&env.shm,
&env.shell,
env.decorations_mgr.as_ref(),
implementation,
)
}
pub fn init<Impl>(
surface: Proxy<wl_surface::WlSurface>,
initial_dims: (u32, u32),
compositor: &Proxy<wl_compositor::WlCompositor>,
subcompositor: &Proxy<wl_subcompositor::WlSubcompositor>,
shm: &Proxy<wl_shm::WlShm>,
shell: &Shell,
implementation: Impl,
) -> Result<Window<F>, F::Error>
where
Impl: FnMut(Event) + Send + 'static,
{
Self::init_with_decorations(
surface,
initial_dims,
compositor,
subcompositor,
shm,
shell,
None,
implementation,
)
}
pub fn init_with_decorations<Impl>(
surface: Proxy<wl_surface::WlSurface>,
initial_dims: (u32, u32),
compositor: &Proxy<wl_compositor::WlCompositor>,
subcompositor: &Proxy<wl_subcompositor::WlSubcompositor>,
shm: &Proxy<wl_shm::WlShm>,
shell: &Shell,
decoration_mgr: Option<&Proxy<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>>,
implementation: Impl,
) -> Result<Window<F>, F::Error>
where
Impl: FnMut(Event) + Send + 'static,
{
let inner = Arc::new(Mutex::new(None::<WindowInner<F>>));
let frame_inner = inner.clone();
let shell_inner = inner.clone();
let mut frame = F::init(
&surface,
compositor,
subcompositor,
shm,
Box::new(move |req, serial| {
if let Some(ref mut inner) = *shell_inner.lock().unwrap() {
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::Close => (inner.user_impl)(Event::Close),
FrameRequest::Refresh => (inner.user_impl)(Event::Refresh),
}
}
}) as Box<_>,
)?;
frame.resize(initial_dims);
let frame = Arc::new(Mutex::new(frame));
let shell_surface = Arc::new(shell::create_shell_surface(shell, &surface, move |event| {
if let Some(ref mut inner) = *frame_inner.lock().unwrap() {
match event {
shell::Event::Configure {
states,
mut new_size,
} => {
let mut frame = inner.frame.lock().unwrap();
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)
});
let mut need_refresh = false;
need_refresh |= frame.set_maximized(states.contains(&State::Maximized));
if need_refresh {
if states.contains(&State::Maximized) {
inner.old_size = Some(inner.current_size);
} else if new_size.is_none() {
new_size = inner.old_size.take();
}
}
need_refresh |= frame.set_active(states.contains(&State::Activated));
if need_refresh {
(inner.user_impl)(Event::Refresh);
}
(inner.user_impl)(Event::Configure { states, new_size });
}
shell::Event::Close => {
(inner.user_impl)(Event::Close);
}
}
}
}));
{
let frame = frame.lock().unwrap();
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);
}
*(inner.lock().unwrap()) = 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,
});
let window = Window {
frame,
shell_surface,
decoration: Mutex::new(None),
decoration_mgr: decoration_mgr.cloned(),
surface,
inner,
};
{
let mut decoration = window.decoration.lock().unwrap();
window.ensure_decoration(&mut decoration);
}
Ok(window)
}
fn ensure_decoration(
&self,
decoration: &mut Option<Proxy<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>>,
) {
if self.decoration_mgr.is_none() {
return;
}
if let Some(ref decoration) = *decoration {
if decoration.is_alive() {
return;
}
}
let decoration_frame = self.frame.clone();
let decoration_inner = self.inner.clone();
*decoration = match (self.shell_surface.get_xdg(), &self.decoration_mgr) {
(Some(toplevel), &Some(ref mgr)) => {
use self::zxdg_toplevel_decoration_v1::{Event, Mode};
mgr.get_toplevel_decoration(toplevel, |newdec| {
newdec.implement(
move |event, _| {
let Event::Configure { mode } = event;
match mode {
Mode::ServerSide => {
decoration_frame.lock().unwrap().set_hidden(true);
}
Mode::ClientSide => {
let want_decorate = decoration_inner
.lock()
.unwrap()
.as_ref()
.map(|inner| inner.decorated)
.unwrap_or(false);
decoration_frame.lock().unwrap().set_hidden(!want_decorate);
}
}
},
(),
)
})
.ok()
.map(|decoration| {
decoration.set_mode(Mode::ServerSide);
decoration
})
}
_ => None,
};
}
pub fn new_seat(&mut self, seat: &Proxy<wl_seat::WlSeat>) {
self.frame.lock().unwrap().new_seat(seat);
}
pub fn surface(&self) -> &Proxy<wl_surface::WlSurface> {
&self.surface
}
pub fn refresh(&mut self) {
self.frame.lock().unwrap().redraw();
}
pub fn set_title(&self, title: String) {
self.frame.lock().unwrap().set_title(title.clone());
self.shell_surface.set_title(title);
}
pub fn set_app_id(&self, app_id: String) {
self.shell_surface.set_app_id(app_id);
}
pub fn set_decorate(&self, decorate: bool) {
self.frame.lock().unwrap().set_hidden(!decorate);
let mut decoration_guard = self.decoration.lock().unwrap();
self.ensure_decoration(&mut decoration_guard);
if let Some(ref dec) = *decoration_guard {
if decorate {
dec.unset_mode();
} else {
dec.destroy();
}
}
}
pub fn set_resizable(&self, resizable: bool) {
let mut frame = self.frame.lock().unwrap();
frame.set_resizable(resizable);
let mut inner = self.inner.lock().unwrap();
if let Some(ref mut inner) = *inner {
if resizable {
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 {
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)));
}
}
}
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.lock().unwrap() {
inner.current_size = (w, h);
}
let mut frame = self.frame.lock().unwrap();
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);
}
pub fn set_maximized(&self) {
self.shell_surface.set_maximized();
}
pub fn unset_maximized(&self) {
self.shell_surface.unset_maximized();
}
pub fn set_minimized(&self) {
self.shell_surface.set_minimized();
}
pub fn set_fullscreen(&self, output: Option<&Proxy<wl_output::WlOutput>>) {
self.shell_surface.set_fullscreen(output);
}
pub fn unset_fullscreen(&self) {
self.shell_surface.unset_fullscreen();
}
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.lock().unwrap().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.lock().unwrap()) {
inner.min_size = size.unwrap_or(MIN_WINDOW_SIZE)
}
}
pub fn set_max_size(&mut self, size: Option<(u32, u32)>) {
let max_size =
size.map(|(w, h)| self.frame.lock().unwrap().add_borders(w as i32, h as i32));
self.shell_surface.set_max_size(max_size);
if let Some(ref mut inner) = *(self.inner.lock().unwrap()) {
inner.max_size = size.map(|(w, h)| (w as u32, h as u32));
}
}
pub fn set_theme<T: Theme>(&mut self, theme: T) {
self.frame.lock().unwrap().set_theme(theme)
}
}
impl<F: Frame> Drop for Window<F> {
fn drop(&mut self) {
self.inner.lock().unwrap().take();
}
}
pub enum FrameRequest {
Minimize,
Maximize,
UnMaximize,
Close,
Move(Proxy<wl_seat::WlSeat>),
Resize(Proxy<wl_seat::WlSeat>, ResizeEdge),
Refresh,
}
pub trait Frame: Sized + Send {
type Error;
fn init(
base_surface: &Proxy<wl_surface::WlSurface>,
compositor: &Proxy<wl_compositor::WlCompositor>,
subcompositor: &Proxy<wl_subcompositor::WlSubcompositor>,
shm: &Proxy<wl_shm::WlShm>,
implementation: Box<FnMut(FrameRequest, u32) + Send>,
) -> Result<Self, Self::Error>;
fn set_active(&mut self, active: bool) -> bool;
fn set_maximized(&mut self, maximized: bool) -> bool;
fn set_hidden(&mut self, hidden: bool);
fn set_resizable(&mut self, resizable: bool);
fn new_seat(&mut self, seat: &Proxy<wl_seat::WlSeat>);
fn resize(&mut self, newsize: (u32, u32));
fn redraw(&mut self);
fn subtract_borders(&self, width: i32, height: i32) -> (i32, i32);
fn add_borders(&self, width: i32, height: i32) -> (i32, i32);
fn location(&self) -> (i32, i32) {
(0, 0)
}
fn set_theme<T: Theme>(&mut self, theme: T);
fn set_title(&mut self, title: String);
}