//! XDG Dialog Windows //! //! This interface allows a compositor to announce support for xdg-dialog toplevel hints, eg. modal hint. //! //! ```no_run //! # extern crate wayland_server; //! # //! use smithay::{delegate_xdg_dialog, delegate_xdg_shell}; //! use smithay::wayland::shell::xdg::{ToplevelSurface, XdgShellHandler}; //! # use smithay::utils::Serial; //! # use smithay::wayland::shell::xdg::{XdgShellState, PopupSurface, PositionerState}; //! # use smithay::reexports::wayland_server::protocol::{wl_seat, wl_surface}; //! use smithay::wayland::shell::xdg::dialog::{XdgDialogState, XdgDialogHandler}; //! //! # struct State { dialog_state: XdgDialogState, seat_state: SeatState } //! # let mut display = wayland_server::Display::::new().unwrap(); //! //! // Create a dialog state //! let dialog_state = XdgDialogState::new::( //! &display.handle(), //! ); //! //! // store that state inside your compositor state //! // ... //! //! // implement the necessary traits //! impl XdgShellHandler for State { //! # fn xdg_shell_state(&mut self) -> &mut XdgShellState { unimplemented!() } //! # fn new_toplevel(&mut self, surface: ToplevelSurface) { unimplemented!() } //! # fn new_popup( //! # &mut self, //! # surface: PopupSurface, //! # positioner: PositionerState, //! # ) { unimplemented!() } //! # fn grab( //! # &mut self, //! # surface: PopupSurface, //! # seat: wl_seat::WlSeat, //! # serial: Serial, //! # ) { unimplemented!() } //! # fn reposition_request( //! # &mut self, //! # surface: PopupSurface, //! # positioner: PositionerState, //! # token: u32, //! # ) { unimplemented!() } //! // ... //! } //! impl XdgDialogHandler for State { //! fn modal_changed(&mut self, toplevel: ToplevelSurface, is_modal: bool) { /* ... */ } //! } //! //! use smithay::input::{Seat, SeatState, SeatHandler, pointer::CursorImageStatus}; //! //! type Target = wl_surface::WlSurface; //! impl SeatHandler for State { //! type KeyboardFocus = Target; //! type PointerFocus = Target; //! type TouchFocus = Target; //! //! fn seat_state(&mut self) -> &mut SeatState { //! &mut self.seat_state //! } //! //! fn focus_changed(&mut self, seat: &Seat, focused: Option<&Target>) { //! // handle focus changes, if you need to ... //! } //! fn cursor_image(&mut self, seat: &Seat, image: CursorImageStatus) { //! // handle new images for the cursor ... //! } //! } //! delegate_xdg_shell!(State); //! delegate_xdg_dialog!(State); //! //! // You are ready to go! use wayland_protocols::xdg::dialog::v1::server::{ xdg_dialog_v1::{self, XdgDialogV1}, xdg_wm_dialog_v1::{self, XdgWmDialogV1}, }; use wayland_server::{ backend::GlobalId, protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, }; use super::{ToplevelSurface, XdgShellHandler}; use crate::wayland::{ compositor, shell::xdg::{XdgShellSurfaceUserData, XdgToplevelSurfaceData}, }; /// Delegate type for handling xdg dialog events. #[derive(Debug)] pub struct XdgDialogState { global: GlobalId, } impl XdgDialogState { /// Creates a new delegate type for handling xdg dialog events. /// /// A global id is also returned to allow destroying the global in the future. pub fn new(display: &DisplayHandle) -> XdgDialogState where D: GlobalDispatch + Dispatch + 'static, { let global = display.create_global::(1, ()); XdgDialogState { global } } /// Returns the xdg-wm-dialog global. pub fn global(&self) -> GlobalId { self.global.clone() } } /// Handler trait for xdg dialog events. pub trait XdgDialogHandler: XdgShellHandler + GlobalDispatch + Dispatch + Dispatch { /// Does client want to be presented as a modal dialog fn modal_changed(&mut self, toplevel: ToplevelSurface, is_modal: bool) { let _ = toplevel; let _ = is_modal; } } /// Macro to delegate implementation of the xdg dialog to [`XdgDialogState`]. /// /// You must also implement [`XdgDialogHandler`] to use this. #[macro_export] macro_rules! delegate_xdg_dialog { ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { $crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ $crate::reexports::wayland_protocols::xdg::dialog::v1::server::xdg_wm_dialog_v1::XdgWmDialogV1: () ] => $crate::wayland::shell::xdg::dialog::XdgDialogState); $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ $crate::reexports::wayland_protocols::xdg::dialog::v1::server::xdg_wm_dialog_v1::XdgWmDialogV1: () ] => $crate::wayland::shell::xdg::dialog::XdgDialogState); $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ $crate::reexports::wayland_protocols::xdg::dialog::v1::server::xdg_dialog_v1::XdgDialogV1: $crate::wayland::shell::xdg::ToplevelSurface ] => $crate::wayland::shell::xdg::dialog::XdgDialogState); }; } // xdg_wm_dialog_v1 impl GlobalDispatch for XdgDialogState { fn bind( _: &mut D, _: &DisplayHandle, _: &Client, resource: New, _: &(), data_init: &mut DataInit<'_, D>, ) { data_init.init(resource, ()); } } impl Dispatch for XdgDialogState { fn request( state: &mut D, _: &Client, resource: &XdgWmDialogV1, request: xdg_wm_dialog_v1::Request, _: &(), _dh: &DisplayHandle, data_init: &mut DataInit<'_, D>, ) { use xdg_wm_dialog_v1::Request; match request { Request::GetXdgDialog { id, toplevel } => { let data = toplevel.data::().unwrap(); let mut dialog_guard = data.dialog.lock().unwrap(); if dialog_guard.is_some() { resource.post_error( xdg_wm_dialog_v1::Error::AlreadyUsed, "toplevel dialog is already constructed", ); return; } let toplevel = state.xdg_shell_state().get_toplevel(&toplevel).unwrap(); let toplevel_dialog = data_init.init(id, toplevel.clone()); *dialog_guard = Some(toplevel_dialog); drop(dialog_guard); } Request::Destroy => {} _ => unreachable!(), } } } // xdg_dialog_v1 impl Dispatch for XdgDialogState { fn request( state: &mut D, _: &Client, _: &XdgDialogV1, request: xdg_dialog_v1::Request, toplevel: &ToplevelSurface, _dh: &DisplayHandle, _: &mut DataInit<'_, D>, ) { use xdg_dialog_v1::Request; match request { Request::SetModal => { if set_modal(toplevel.wl_surface()) { state.modal_changed(toplevel.clone(), true); } } Request::UnsetModal => { if unset_modal(toplevel.wl_surface()) { state.modal_changed(toplevel.clone(), false); } } Request::Destroy => { if unset_modal(toplevel.wl_surface()) { state.modal_changed(toplevel.clone(), false); } if let Some(data) = toplevel.xdg_toplevel().data::() { data.dialog.lock().unwrap().take(); } } _ => unreachable!(), } } } /// Returns true if changed fn set_modal(wl_surface: &WlSurface) -> bool { compositor::with_states(wl_surface, |states| { let role = &mut states .data_map .get::() .unwrap() .lock() .unwrap(); if role.modal { false } else { role.modal = true; true } }) } /// Returns true if changed fn unset_modal(wl_surface: &WlSurface) -> bool { compositor::with_states(wl_surface, |states| { let role = &mut states .data_map .get::() .unwrap() .lock() .unwrap(); if role.modal { role.modal = false; true } else { false } }) }