use crate::reexports::client::{protocol::wl_compositor::WlCompositor, Proxy, QueueHandle};
use crate::reexports::client::{protocol::wl_surface, Connection, Dispatch};
use crate::reexports::protocols::xdg::decoration::zv1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1;
use crate::shell::xdg::window::inner::{
determine_decoration_mode, determine_window_state, determine_wm_capabilities, WindowInner,
};
use crate::shell::xdg::window::WindowConfigure;
use crate::shell::xdg::Dispatch2;
use crate::shell::xdg::WindowDecorations;
use crate::shell::WaylandSurface;
use crate::{
compositor::{Surface, SurfaceData},
globals::ProvidesBoundGlobal,
};
use crate::{error::GlobalError, shell::xdg::XdgShellSurface};
use std::num::NonZeroU32;
use std::sync::{Arc, Mutex, Weak};
use wayland_protocols::xdg::{
decoration::zv1::client::zxdg_toplevel_decoration_v1,
dialog::v1::client::xdg_dialog_v1::XdgDialogV1, shell::client::xdg_wm_base,
};
use wayland_protocols::xdg::{dialog::v1::client::xdg_dialog_v1, shell::client::xdg_surface};
use wayland_protocols::xdg::{dialog::v1::client::xdg_wm_dialog_v1, shell::client::xdg_toplevel};
pub trait DialogHandler: Sized {
fn request_close(&mut self, conn: &Connection, qh: &QueueHandle<Self>, window: &Dialog);
fn configure(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
window: &Dialog,
configure: WindowConfigure,
serial: u32,
);
}
#[derive(Debug, Clone)]
pub struct Dialog {
inner: Arc<DialogInner>,
}
#[derive(Debug)]
pub struct DialogData(pub(crate) Weak<DialogInner>);
#[derive(Debug)]
pub(crate) struct DialogInner {
pub xdg_dialog: XdgDialogV1,
pub window: WindowInner,
}
impl Dialog {
pub fn new<D, GLOBAL>(
parent: &xdg_toplevel::XdgToplevel,
qh: &QueueHandle<D>,
compositor: &impl ProvidesBoundGlobal<WlCompositor, 6>,
wm: &GLOBAL,
decoration_manager: Option<&ZxdgDecorationManagerV1>,
decorations: WindowDecorations,
) -> Result<Self, GlobalError>
where
D: Dispatch<wl_surface::WlSurface, SurfaceData<()>>
+ Dispatch<xdg_surface::XdgSurface, DialogData>
+ Dispatch<xdg_dialog_v1::XdgDialogV1, DialogData>
+ Dispatch<xdg_toplevel::XdgToplevel, DialogData>
+ Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, DialogData>
+ 'static,
GLOBAL: ProvidesBoundGlobal<xdg_wm_dialog_v1::XdgWmDialogV1, 1>
+ ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, 5>,
{
let surface = Surface::new(compositor, qh)?;
let dialog = Self::from_surface(surface, parent, qh, wm, decoration_manager, decorations)?;
dialog.wl_surface().commit();
Ok(dialog)
}
pub fn from_surface<D, GLOBAL>(
surface: impl Into<Surface>,
parent: &xdg_toplevel::XdgToplevel,
qh: &QueueHandle<D>,
wm_base: &GLOBAL,
decoration_manager: Option<&ZxdgDecorationManagerV1>,
decorations: WindowDecorations,
) -> Result<Self, GlobalError>
where
D: Dispatch<xdg_surface::XdgSurface, DialogData>
+ Dispatch<xdg_dialog_v1::XdgDialogV1, DialogData>
+ Dispatch<xdg_toplevel::XdgToplevel, DialogData>
+ Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, DialogData>
+ 'static,
GLOBAL: ProvidesBoundGlobal<xdg_wm_dialog_v1::XdgWmDialogV1, 1>
+ ProvidesBoundGlobal<xdg_wm_base::XdgWmBase, 5>,
{
let surface = surface.into();
let wm_dialog: xdg_wm_dialog_v1::XdgWmDialogV1 = wm_base.bound_global()?;
let wm_base: xdg_wm_base::XdgWmBase = wm_base.bound_global()?;
let freeze = qh.freeze();
let inner = Arc::new_cyclic(|weak| {
let xdg_surface =
wm_base.get_xdg_surface(surface.wl_surface(), qh, DialogData(weak.clone()));
let surface = XdgShellSurface { surface, xdg_surface };
let xdg_toplevel = surface.xdg_surface.get_toplevel(qh, DialogData(weak.clone()));
xdg_toplevel.set_parent(Some(parent));
let xdg_dialog = wm_dialog.get_xdg_dialog(&xdg_toplevel, qh, DialogData(weak.clone()));
let toplevel_decoration = crate::shell::xdg::XdgShell::toplevel_decoration(
decoration_manager,
&xdg_toplevel,
decorations,
DialogData(weak.clone()),
qh,
);
DialogInner {
xdg_dialog,
window: WindowInner {
xdg_surface: surface,
xdg_toplevel,
toplevel_decoration,
pending_configure: Mutex::new(Default::default()),
},
}
});
drop(freeze);
let dialog = Dialog { inner };
Ok(dialog)
}
pub fn from_xdg_toplevel(toplevel: &xdg_toplevel::XdgToplevel) -> Option<Dialog> {
toplevel.data::<DialogData>().and_then(|data| data.dialog())
}
pub fn from_xdg_surface(surface: &xdg_surface::XdgSurface) -> Option<Dialog> {
surface.data::<DialogData>().and_then(|data| data.dialog())
}
pub fn from_toplevel_decoration(
decoration: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
) -> Option<Dialog> {
decoration.data::<DialogData>().and_then(|data| data.dialog())
}
pub fn xdg_dialog(&self) -> &XdgDialogV1 {
&self.inner.xdg_dialog
}
pub fn xdg_shell_surface(&self) -> &XdgShellSurface {
&self.inner.window.xdg_surface
}
pub fn xdg_toplevel(&self) -> &xdg_toplevel::XdgToplevel {
&self.inner.window.xdg_toplevel
}
pub fn xdg_surface(&self) -> &xdg_surface::XdgSurface {
self.inner.window.xdg_surface.xdg_surface()
}
pub fn wl_surface(&self) -> &wl_surface::WlSurface {
self.inner.window.xdg_surface.wl_surface()
}
pub fn set_modal(&self, modal: bool) {
if modal {
self.inner.xdg_dialog.set_modal();
} else {
self.inner.xdg_dialog.unset_modal();
}
}
}
impl WaylandSurface for Dialog {
fn wl_surface(&self) -> &wl_surface::WlSurface {
self.wl_surface()
}
}
impl PartialEq for Dialog {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.inner, &other.inner)
}
}
impl DialogData {
pub fn dialog(&self) -> Option<Dialog> {
let inner = self.0.upgrade()?;
Some(Dialog { inner })
}
}
impl Drop for DialogInner {
fn drop(&mut self) {
self.xdg_dialog.destroy();
}
}
impl<D: DialogHandler> Dispatch2<xdg_surface::XdgSurface, D> for DialogData {
fn event(
&self,
data: &mut D,
xdg_surface: &xdg_surface::XdgSurface,
event: <xdg_surface::XdgSurface as wayland_client::Proxy>::Event,
conn: &Connection,
qhandle: &QueueHandle<D>,
) {
if let Some(dialog) = Dialog::from_xdg_surface(xdg_surface) {
match event {
xdg_surface::Event::Configure { serial } => {
xdg_surface.ack_configure(serial);
let configure = dialog.inner.window.pending_configure.lock().unwrap().clone();
DialogHandler::configure(data, conn, qhandle, &dialog, configure, serial)
}
_ => unreachable!(),
}
}
}
}
impl<D> Dispatch2<XdgDialogV1, D> for DialogData {
fn event(
&self,
_state: &mut D,
_proxy: &XdgDialogV1,
_event: <XdgDialogV1 as wayland_client::Proxy>::Event,
_conn: &Connection,
_qhandle: &QueueHandle<D>,
) {
}
}
impl<D: DialogHandler> Dispatch2<xdg_toplevel::XdgToplevel, D> for DialogData {
fn event(
&self,
data: &mut D,
toplevel: &xdg_toplevel::XdgToplevel,
event: <xdg_toplevel::XdgToplevel as wayland_client::Proxy>::Event,
conn: &Connection,
qhandle: &QueueHandle<D>,
) {
let Some(dialog) = Dialog::from_xdg_toplevel(toplevel) else {
return;
};
match event {
xdg_toplevel::Event::Configure { width, height, states } => {
let new_state = determine_window_state(&states);
let width = u32::try_from(width).ok().and_then(NonZeroU32::new);
let height = u32::try_from(height).ok().and_then(NonZeroU32::new);
let pending_configure = &mut dialog.inner.window.pending_configure.lock().unwrap();
pending_configure.new_size = (width, height);
pending_configure.state = new_state;
}
xdg_toplevel::Event::Close => {
data.request_close(conn, qhandle, &dialog);
}
xdg_toplevel::Event::ConfigureBounds { width, height } => {
let pending_configure = &mut dialog.inner.window.pending_configure.lock().unwrap();
if width == 0 && height == 0 {
pending_configure.suggested_bounds = None;
} else {
pending_configure.suggested_bounds = Some((width as u32, height as u32));
}
}
xdg_toplevel::Event::WmCapabilities { capabilities } => {
let pending_configure = &mut dialog.inner.window.pending_configure.lock().unwrap();
pending_configure.capabilities = determine_wm_capabilities(&capabilities)
}
_ => unreachable!(),
}
}
}
impl<D> Dispatch2<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, D> for DialogData
where
D: DialogHandler,
{
fn event(
&self,
_: &mut D,
decoration: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
event: zxdg_toplevel_decoration_v1::Event,
_: &Connection,
_: &QueueHandle<D>,
) {
if let Some(dialog) = Dialog::from_toplevel_decoration(decoration) {
match event {
zxdg_toplevel_decoration_v1::Event::Configure { mode } => match mode {
wayland_client::WEnum::Value(mode) => {
let mode = determine_decoration_mode(mode);
dialog.inner.window.pending_configure.lock().unwrap().decoration_mode =
mode;
}
wayland_client::WEnum::Unknown(unknown) => {
log::error!(target: "sctk", "unknown decoration mode 0x{:x}", unknown);
}
},
_ => unreachable!(),
}
}
}
}