use std::os::unix::io::OwnedFd;
use std::sync::{Arc, Mutex};
use wayland_protocols::xdg::dialog::v1::client::xdg_wm_dialog_v1;
use crate::reexports::client::globals::{BindError, GlobalList};
use crate::reexports::client::Connection;
use crate::reexports::client::{protocol::wl_surface, Dispatch, Proxy, QueueHandle};
use crate::reexports::protocols::xdg::decoration::zv1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1;
use crate::reexports::protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::Mode;
use crate::reexports::protocols::xdg::decoration::zv1::client::{
zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,
};
use crate::reexports::protocols::xdg::dialog::v1::client::xdg_dialog_v1;
use crate::reexports::protocols::xdg::shell::client::{
xdg_positioner, xdg_surface, xdg_toplevel, xdg_wm_base,
};
use crate::compositor::Surface;
use crate::dispatch2::Dispatch2;
use crate::error::GlobalError;
use crate::globals::{GlobalData, ProvidesBoundGlobal};
use crate::registry::GlobalProxy;
use crate::shell::xdg::dialog::{Dialog, DialogData, DialogHandler};
use self::window::inner::WindowInner;
use self::window::{Window, WindowData, WindowDecorations, WindowHandler};
use super::WaylandSurface;
pub mod dialog;
pub mod fallback_frame;
pub mod popup;
pub mod window;
#[derive(Debug)]
pub struct XdgShell {
xdg_wm_dialog_v1: Option<xdg_wm_dialog_v1::XdgWmDialogV1>,
xdg_wm_base: xdg_wm_base::XdgWmBase,
xdg_decoration_manager: GlobalProxy<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,
}
impl XdgShell {
pub const API_VERSION_MAX: u32 = 6;
pub fn bind<State>(globals: &GlobalList, qh: &QueueHandle<State>) -> Result<Self, BindError>
where
State: Dispatch<xdg_wm_base::XdgWmBase, GlobalData, State>
+ Dispatch<xdg_wm_dialog_v1::XdgWmDialogV1, GlobalData, State>
+ Dispatch<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, GlobalData, State>
+ 'static,
{
let xdg_wm_base = globals.bind(qh, 1..=Self::API_VERSION_MAX, GlobalData)?;
let xdg_wm_dialog_v1 = globals.bind(qh, 1..=1, GlobalData).ok();
let xdg_decoration_manager = GlobalProxy::from(globals.bind(qh, 1..=1, GlobalData));
Ok(Self { xdg_wm_base, xdg_wm_dialog_v1, xdg_decoration_manager })
}
pub(crate) fn toplevel_decoration<State, D>(
decoration_manager: Option<&ZxdgDecorationManagerV1>,
xdg_toplevel: &xdg_toplevel::XdgToplevel,
decorations: WindowDecorations,
data: D,
qh: &QueueHandle<State>,
) -> Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>
where
D: Send + Sync + 'static,
State: Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, D> + 'static,
{
decoration_manager.and_then(|decoration_manager| {
match decorations {
WindowDecorations::ClientOnly | WindowDecorations::None => None,
_ => {
let toplevel_decoration =
decoration_manager.get_toplevel_decoration(xdg_toplevel, qh, data);
let mode = match decorations {
WindowDecorations::RequestServer => Some(Mode::ServerSide),
WindowDecorations::RequestClient => Some(Mode::ClientSide),
_ => None,
};
if let Some(mode) = mode {
toplevel_decoration.set_mode(mode);
}
Some(toplevel_decoration)
}
}
})
}
#[must_use = "Dropping all window handles will destroy the window"]
pub fn create_window<State>(
&self,
surface: impl Into<Surface>,
decorations: WindowDecorations,
qh: &QueueHandle<State>,
) -> Window
where
State: Dispatch<xdg_surface::XdgSurface, WindowData>
+ Dispatch<xdg_toplevel::XdgToplevel, WindowData>
+ Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, WindowData>
+ WindowHandler
+ 'static,
{
let decoration_manager = self.xdg_decoration_manager.get().ok();
let surface = surface.into();
let freeze = qh.freeze();
let inner = Arc::new_cyclic(|weak| {
let xdg_surface = self.xdg_wm_base.get_xdg_surface(
surface.wl_surface(),
qh,
WindowData(weak.clone()),
);
let xdg_surface = XdgShellSurface { surface, xdg_surface };
let xdg_toplevel = xdg_surface.xdg_surface().get_toplevel(qh, WindowData(weak.clone()));
let toplevel_decoration = Self::toplevel_decoration(
decoration_manager,
&xdg_toplevel,
decorations,
WindowData(weak.clone()),
qh,
);
WindowInner {
xdg_surface,
xdg_toplevel,
toplevel_decoration,
pending_configure: Mutex::new(Default::default()),
}
});
drop(freeze);
Window(inner)
}
#[must_use = "Dropping all dialog handles will destroy the dialog"]
pub fn create_dialog<State>(
&self,
surface: impl Into<Surface>,
decorations: WindowDecorations,
qh: &QueueHandle<State>,
parent: &xdg_toplevel::XdgToplevel,
) -> Result<Dialog, GlobalError>
where
State: Dispatch<xdg_surface::XdgSurface, DialogData>
+ Dispatch<xdg_toplevel::XdgToplevel, DialogData>
+ Dispatch<xdg_dialog_v1::XdgDialogV1, DialogData>
+ Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, DialogData>
+ DialogHandler
+ 'static,
{
let decoration_manager = self.xdg_decoration_manager.get().ok();
Dialog::from_surface(surface, parent, qh, self, decoration_manager, decorations)
}
pub fn xdg_wm_base(&self) -> &xdg_wm_base::XdgWmBase {
&self.xdg_wm_base
}
}
#[derive(Debug)]
pub struct XdgPositioner(xdg_positioner::XdgPositioner);
impl XdgPositioner {
pub fn new(
wm_base: &impl ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, { XdgShell::API_VERSION_MAX }>,
) -> Result<Self, GlobalError> {
wm_base
.bound_global()
.map(|wm_base| {
wm_base
.send_constructor(
xdg_wm_base::Request::CreatePositioner {},
Arc::new(PositionerData),
)
.unwrap_or_else(|_| Proxy::inert(wm_base.backend().clone()))
})
.map(XdgPositioner)
}
}
impl std::ops::Deref for XdgPositioner {
type Target = xdg_positioner::XdgPositioner;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Drop for XdgPositioner {
fn drop(&mut self) {
self.0.destroy()
}
}
struct PositionerData;
impl wayland_client::backend::ObjectData for PositionerData {
fn event(
self: Arc<Self>,
_: &wayland_client::backend::Backend,
_: wayland_client::backend::protocol::Message<wayland_client::backend::ObjectId, OwnedFd>,
) -> Option<Arc<dyn wayland_client::backend::ObjectData + 'static>> {
unreachable!("xdg_positioner has no events");
}
fn destroyed(&self, _: wayland_client::backend::ObjectId) {}
}
#[derive(Debug)]
pub struct XdgShellSurface {
xdg_surface: xdg_surface::XdgSurface,
surface: Surface,
}
impl XdgShellSurface {
pub fn new<U, D>(
wm_base: &impl ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, { XdgShell::API_VERSION_MAX }>,
qh: &QueueHandle<D>,
surface: impl Into<Surface>,
udata: U,
) -> Result<XdgShellSurface, GlobalError>
where
D: Dispatch<xdg_surface::XdgSurface, U> + 'static,
U: Send + Sync + 'static,
{
let surface = surface.into();
let xdg_surface = wm_base.bound_global()?.get_xdg_surface(surface.wl_surface(), qh, udata);
Ok(XdgShellSurface { xdg_surface, surface })
}
pub fn xdg_surface(&self) -> &xdg_surface::XdgSurface {
&self.xdg_surface
}
pub fn wl_surface(&self) -> &wl_surface::WlSurface {
self.surface.wl_surface()
}
}
pub trait XdgSurface: WaylandSurface + Sized {
fn xdg_surface(&self) -> &xdg_surface::XdgSurface;
fn set_window_geometry(&self, x: u32, y: u32, width: u32, height: u32) {
self.xdg_surface().set_window_geometry(x as i32, y as i32, width as i32, height as i32);
}
}
impl WaylandSurface for XdgShellSurface {
fn wl_surface(&self) -> &wl_surface::WlSurface {
self.wl_surface()
}
}
impl XdgSurface for XdgShellSurface {
fn xdg_surface(&self) -> &xdg_surface::XdgSurface {
&self.xdg_surface
}
}
impl Drop for XdgShellSurface {
fn drop(&mut self) {
self.xdg_surface.destroy();
}
}
impl ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, 5> for XdgShell {
fn bound_global(&self) -> Result<xdg_wm_base::XdgWmBase, GlobalError> {
<Self as ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, 6>>::bound_global(self)
}
}
impl ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, { XdgShell::API_VERSION_MAX }> for XdgShell {
fn bound_global(&self) -> Result<xdg_wm_base::XdgWmBase, GlobalError> {
Ok(self.xdg_wm_base.clone())
}
}
impl ProvidesBoundGlobal<xdg_wm_dialog_v1::XdgWmDialogV1, 1> for XdgShell {
fn bound_global(&self) -> Result<xdg_wm_dialog_v1::XdgWmDialogV1, GlobalError> {
self.xdg_wm_dialog_v1
.clone()
.ok_or(GlobalError::MissingGlobal("Dialog v1 is not available"))
}
}
impl<D> Dispatch2<xdg_wm_dialog_v1::XdgWmDialogV1, D> for GlobalData {
fn event(
&self,
_: &mut D,
_: &xdg_wm_dialog_v1::XdgWmDialogV1,
_: xdg_wm_dialog_v1::Event,
_: &Connection,
_: &QueueHandle<D>,
) {
unreachable!("xdg_wm_dialog_v1 has no events")
}
}
impl<D> Dispatch2<xdg_wm_base::XdgWmBase, D> for GlobalData {
fn event(
&self,
_state: &mut D,
xdg_wm_base: &xdg_wm_base::XdgWmBase,
event: xdg_wm_base::Event,
_conn: &Connection,
_qh: &QueueHandle<D>,
) {
match event {
xdg_wm_base::Event::Ping { serial } => {
xdg_wm_base.pong(serial);
}
_ => unreachable!(),
}
}
}