wayland.shell.xdg: review docs & API

This commit is contained in:
Victor Berger 2021-07-01 23:07:07 +02:00 committed by Victor Berger
parent ba6bef3e85
commit e96272cae5
6 changed files with 246 additions and 177 deletions

View File

@ -352,7 +352,7 @@ pub fn init_shell<BackendData: 'static>(display: Rc<RefCell<Display>>, log: ::sl
let xdg_output_map = output_map.clone(); let xdg_output_map = output_map.clone();
let (xdg_shell_state, _, _) = xdg_shell_init( let (xdg_shell_state, _, _) = xdg_shell_init(
&mut *display.borrow_mut(), &mut *display.borrow_mut(),
move |shell_event| match shell_event { move |shell_event, _dispatch_data| match shell_event {
XdgRequest::NewToplevel { surface } => { XdgRequest::NewToplevel { surface } => {
// place the window at a random location on the primary output // place the window at a random location on the primary output
// or if there is not output in a [0;800]x[0;800] square // or if there is not output in a [0;800]x[0;800] square

View File

@ -17,7 +17,7 @@ use crate::shell::SurfaceData;
#[cfg(feature = "xwayland")] #[cfg(feature = "xwayland")]
use crate::xwayland::X11Surface; use crate::xwayland::X11Surface;
#[derive(Clone)] #[derive(Clone, PartialEq)]
pub enum Kind { pub enum Kind {
Xdg(ToplevelSurface), Xdg(ToplevelSurface),
Wl(ShellSurface), Wl(ShellSurface),
@ -44,17 +44,6 @@ impl Kind {
} }
} }
/// Do this handle and the other one actually refer to the same toplevel surface?
pub fn equals(&self, other: &Self) -> bool {
match (self, other) {
(Kind::Xdg(a), Kind::Xdg(b)) => a.equals(b),
(Kind::Wl(a), Kind::Wl(b)) => a.equals(b),
#[cfg(feature = "xwayland")]
(Kind::X11(a), Kind::X11(b)) => a.equals(b),
_ => false,
}
}
/// Activate/Deactivate this window /// Activate/Deactivate this window
pub fn set_activated(&self, active: bool) { pub fn set_activated(&self, active: bool) {
if let Kind::Xdg(ref t) = self { if let Kind::Xdg(ref t) = self {
@ -357,7 +346,7 @@ impl WindowMap {
/// Refreshes the state of the toplevel, if it exists. /// Refreshes the state of the toplevel, if it exists.
pub fn refresh_toplevel(&mut self, toplevel: &Kind) { pub fn refresh_toplevel(&mut self, toplevel: &Kind) {
if let Some(w) = self.windows.iter_mut().find(|w| w.toplevel.equals(toplevel)) { if let Some(w) = self.windows.iter_mut().find(|w| &w.toplevel == toplevel) {
w.self_update(); w.self_update();
} }
} }
@ -400,13 +389,13 @@ impl WindowMap {
pub fn location(&self, toplevel: &Kind) -> Option<(i32, i32)> { pub fn location(&self, toplevel: &Kind) -> Option<(i32, i32)> {
self.windows self.windows
.iter() .iter()
.find(|w| w.toplevel.equals(toplevel)) .find(|w| &w.toplevel == toplevel)
.map(|w| w.location) .map(|w| w.location)
} }
/// Sets the location of the toplevel, if it exists. /// Sets the location of the toplevel, if it exists.
pub fn set_location(&mut self, toplevel: &Kind, location: (i32, i32)) { pub fn set_location(&mut self, toplevel: &Kind, location: (i32, i32)) {
if let Some(w) = self.windows.iter_mut().find(|w| w.toplevel.equals(toplevel)) { if let Some(w) = self.windows.iter_mut().find(|w| &w.toplevel == toplevel) {
w.location = location; w.location = location;
w.self_update(); w.self_update();
} }
@ -416,7 +405,7 @@ impl WindowMap {
pub fn geometry(&self, toplevel: &Kind) -> Option<Rectangle> { pub fn geometry(&self, toplevel: &Kind) -> Option<Rectangle> {
self.windows self.windows
.iter() .iter()
.find(|w| w.toplevel.equals(toplevel)) .find(|w| &w.toplevel == toplevel)
.map(|w| w.geometry()) .map(|w| w.geometry())
} }

View File

@ -237,15 +237,17 @@ pub struct X11Surface {
surface: WlSurface, surface: WlSurface,
} }
impl std::cmp::PartialEq for X11Surface {
fn eq(&self, other: &Self) -> bool {
self.alive() && other.alive() && self.surface == other.surface
}
}
impl X11Surface { impl X11Surface {
pub fn alive(&self) -> bool { pub fn alive(&self) -> bool {
self.surface.as_ref().is_alive() self.surface.as_ref().is_alive()
} }
pub fn equals(&self, other: &Self) -> bool {
self.alive() && other.alive() && self.surface.as_ref().equals(&other.surface.as_ref())
}
pub fn get_surface(&self) -> Option<&WlSurface> { pub fn get_surface(&self) -> Option<&WlSurface> {
if self.alive() { if self.alive() {
Some(&self.surface) Some(&self.surface)

View File

@ -22,24 +22,19 @@
//! ### Initialization //! ### Initialization
//! //!
//! To initialize this handler, simple use the [`xdg_shell_init`] function provided in this module. //! To initialize this handler, simple use the [`xdg_shell_init`] function provided in this module.
//! You need to provide a closure that will be invoked whenever some action is required from you,
//! are represented by the [`XdgRequest`] enum.
//! //!
//! ```no_run //! ```no_run
//! # extern crate wayland_server; //! # extern crate wayland_server;
//! # //! #
//! use smithay::wayland::shell::xdg::{xdg_shell_init, XdgRequest}; //! use smithay::wayland::shell::xdg::{xdg_shell_init, XdgRequest};
//! //!
//!
//! // define the metadata you want associated with the shell clients
//! #[derive(Default)]
//! struct MyShellData {
//! /* ... */
//! }
//!
//! # let mut display = wayland_server::Display::new(); //! # let mut display = wayland_server::Display::new();
//! let (shell_state, _, _) = xdg_shell_init( //! let (shell_state, _, _) = xdg_shell_init(
//! &mut display, //! &mut display,
//! // your implementation //! // your implementation
//! |event: XdgRequest| { /* ... */ }, //! |event: XdgRequest, dispatch_data| { /* handle the shell requests here */ },
//! None // put a logger if you want //! None // put a logger if you want
//! ); //! );
//! //!
@ -52,8 +47,7 @@
//! //!
//! - [`ShellClient`]: //! - [`ShellClient`]:
//! This is a handle representing an instantiation of a shell global //! This is a handle representing an instantiation of a shell global
//! you can associate client-wise metadata to it (this is the `MyShellData` type in //! you can associate client-wise metadata to it through an [`UserDataMap`].
//! the example above).
//! - [`ToplevelSurface`]: //! - [`ToplevelSurface`]:
//! This is a handle representing a toplevel surface, you can //! This is a handle representing a toplevel surface, you can
//! retrieve a list of all currently alive toplevel surface from the //! retrieve a list of all currently alive toplevel surface from the
@ -84,6 +78,7 @@ use wayland_protocols::{
unstable::xdg_shell::v6::server::{zxdg_popup_v6, zxdg_shell_v6, zxdg_toplevel_v6}, unstable::xdg_shell::v6::server::{zxdg_popup_v6, zxdg_shell_v6, zxdg_toplevel_v6},
xdg_shell::server::{xdg_popup, xdg_positioner, xdg_toplevel, xdg_wm_base}, xdg_shell::server::{xdg_popup, xdg_positioner, xdg_toplevel, xdg_wm_base},
}; };
use wayland_server::DispatchData;
use wayland_server::{ use wayland_server::{
protocol::{wl_output, wl_seat, wl_surface}, protocol::{wl_output, wl_seat, wl_surface},
Display, Filter, Global, UserDataMap, Display, Filter, Global, UserDataMap,
@ -646,7 +641,7 @@ impl Cacheable for SurfaceCachedState {
pub(crate) struct ShellData { pub(crate) struct ShellData {
log: ::slog::Logger, log: ::slog::Logger,
user_impl: Rc<RefCell<dyn FnMut(XdgRequest)>>, user_impl: Rc<RefCell<dyn FnMut(XdgRequest, DispatchData<'_>)>>,
shell_state: Arc<Mutex<ShellState>>, shell_state: Arc<Mutex<ShellState>>,
} }
@ -672,7 +667,7 @@ pub fn xdg_shell_init<L, Impl>(
) )
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
Impl: FnMut(XdgRequest) + 'static, Impl: FnMut(XdgRequest, DispatchData<'_>) + 'static,
{ {
let log = crate::slog_or_fallback(logger); let log = crate::slog_or_fallback(logger);
let shell_state = Arc::new(Mutex::new(ShellState { let shell_state = Arc::new(Mutex::new(ShellState {
@ -690,15 +685,15 @@ where
let xdg_shell_global = display.create_global( let xdg_shell_global = display.create_global(
1, 1,
Filter::new(move |(shell, _version), _, _data| { Filter::new(move |(shell, _version), _, dispatch_data| {
self::xdg_handlers::implement_wm_base(shell, &shell_data); self::xdg_handlers::implement_wm_base(shell, &shell_data, dispatch_data);
}), }),
); );
let zxdgv6_shell_global = display.create_global( let zxdgv6_shell_global = display.create_global(
1, 1,
Filter::new(move |(shell, _version), _, _data| { Filter::new(move |(shell, _version), _, dispatch_data| {
self::zxdgv6_handlers::implement_shell(shell, &shell_data_z); self::zxdgv6_handlers::implement_shell(shell, &shell_data_z, dispatch_data);
}), }),
); );
@ -764,6 +759,16 @@ pub struct ShellClient {
kind: ShellClientKind, kind: ShellClientKind,
} }
impl std::cmp::PartialEq for ShellClient {
fn eq(&self, other: &Self) -> bool {
match (&self.kind, &other.kind) {
(&ShellClientKind::Xdg(ref s1), &ShellClientKind::Xdg(ref s2)) => s1 == s2,
(&ShellClientKind::ZxdgV6(ref s1), &ShellClientKind::ZxdgV6(ref s2)) => s1 == s2,
_ => false,
}
}
}
impl ShellClient { impl ShellClient {
/// Is the shell client represented by this handle still connected? /// Is the shell client represented by this handle still connected?
pub fn alive(&self) -> bool { pub fn alive(&self) -> bool {
@ -773,18 +778,6 @@ impl ShellClient {
} }
} }
/// Checks if this handle and the other one actually refer to the
/// same shell client
pub fn equals(&self, other: &Self) -> bool {
match (&self.kind, &other.kind) {
(&ShellClientKind::Xdg(ref s1), &ShellClientKind::Xdg(ref s2)) => s1.as_ref().equals(s2.as_ref()),
(&ShellClientKind::ZxdgV6(ref s1), &ShellClientKind::ZxdgV6(ref s2)) => {
s1.as_ref().equals(s2.as_ref())
}
_ => false,
}
}
/// Send a ping request to this shell client /// Send a ping request to this shell client
/// ///
/// You'll receive the reply as a [`XdgRequest::ClientPong`] request. /// You'll receive the reply as a [`XdgRequest::ClientPong`] request.
@ -873,6 +866,12 @@ pub struct ToplevelSurface {
shell_surface: ToplevelKind, shell_surface: ToplevelKind,
} }
impl std::cmp::PartialEq for ToplevelSurface {
fn eq(&self, other: &Self) -> bool {
self.alive() && other.alive() && self.wl_surface == other.wl_surface
}
}
impl ToplevelSurface { impl ToplevelSurface {
/// Is the toplevel surface referred by this handle still alive? /// Is the toplevel surface referred by this handle still alive?
pub fn alive(&self) -> bool { pub fn alive(&self) -> bool {
@ -883,11 +882,6 @@ impl ToplevelSurface {
shell_alive && self.wl_surface.as_ref().is_alive() shell_alive && self.wl_surface.as_ref().is_alive()
} }
/// Do this handle and the other one actually refer to the same toplevel surface?
pub fn equals(&self, other: &Self) -> bool {
self.alive() && other.alive() && self.wl_surface.as_ref().equals(&other.wl_surface.as_ref())
}
/// Retrieve the shell client owning this toplevel surface /// Retrieve the shell client owning this toplevel surface
/// ///
/// Returns `None` if the surface does actually no longer exist. /// Returns `None` if the surface does actually no longer exist.
@ -957,6 +951,9 @@ impl ToplevelSurface {
/// Send a configure event to this toplevel surface to suggest it a new configuration /// Send a configure event to this toplevel surface to suggest it a new configuration
/// ///
/// The serial of this configure will be tracked waiting for the client to ACK it. /// The serial of this configure will be tracked waiting for the client to ACK it.
///
/// You can manipulate the state that will be sent to the client with the [`with_pending_state`](#method.with_pending_state)
/// method.
pub fn send_configure(&self) { pub fn send_configure(&self) {
if let Some(surface) = self.get_surface() { if let Some(surface) = self.get_surface() {
let configure = compositor::with_states(surface, |states| { let configure = compositor::with_states(surface, |states| {
@ -1084,13 +1081,10 @@ impl ToplevelSurface {
/// Allows the pending state of this toplevel to /// Allows the pending state of this toplevel to
/// be manipulated. /// be manipulated.
/// ///
/// This should be used to inform the client about /// This should be used to inform the client about size and state changes,
/// size and state changes. /// for example after a resize request from the client.
/// ///
/// For example after a resize request from the client. /// The state will be sent to the client when calling [`send_configure`](#method.send_configure).
///
/// The state will be sent to the client when calling
/// send_configure.
pub fn with_pending_state<F, T>(&self, f: F) -> Result<T, DeadResource> pub fn with_pending_state<F, T>(&self, f: F) -> Result<T, DeadResource>
where where
F: FnOnce(&mut ToplevelState) -> T, F: FnOnce(&mut ToplevelState) -> T,
@ -1157,6 +1151,12 @@ pub struct PopupSurface {
shell_surface: PopupKind, shell_surface: PopupKind,
} }
impl std::cmp::PartialEq for PopupSurface {
fn eq(&self, other: &Self) -> bool {
self.alive() && other.alive() && self.wl_surface == other.wl_surface
}
}
impl PopupSurface { impl PopupSurface {
/// Is the popup surface referred by this handle still alive? /// Is the popup surface referred by this handle still alive?
pub fn alive(&self) -> bool { pub fn alive(&self) -> bool {
@ -1187,11 +1187,6 @@ impl PopupSurface {
} }
} }
/// Do this handle and the other one actually refer to the same popup surface?
pub fn equals(&self, other: &Self) -> bool {
self.alive() && other.alive() && self.wl_surface.as_ref().equals(&other.wl_surface.as_ref())
}
/// Retrieve the shell client owning this popup surface /// Retrieve the shell client owning this popup surface
/// ///
/// Returns `None` if the surface does actually no longer exist. /// Returns `None` if the surface does actually no longer exist.
@ -1225,6 +1220,9 @@ impl PopupSurface {
/// Send a configure event to this popup surface to suggest it a new configuration /// Send a configure event to this popup surface to suggest it a new configuration
/// ///
/// The serial of this configure will be tracked waiting for the client to ACK it. /// The serial of this configure will be tracked waiting for the client to ACK it.
///
/// You can manipulate the state that will be sent to the client with the [`with_pending_state`](#method.with_pending_state)
/// method.
pub fn send_configure(&self) { pub fn send_configure(&self) {
if let Some(surface) = self.get_surface() { if let Some(surface) = self.get_surface() {
let next_configure = compositor::with_states(surface, |states| { let next_configure = compositor::with_states(surface, |states| {
@ -1406,13 +1404,10 @@ impl PopupSurface {
/// Allows the pending state of this popup to /// Allows the pending state of this popup to
/// be manipulated. /// be manipulated.
/// ///
/// This should be used to inform the client about /// This should be used to inform the client about size and position changes,
/// size and position changes. /// for example after a move of the parent toplevel.
/// ///
/// For example after a move of the parent toplevel. /// The state will be sent to the client when calling [`send_configure`](#method.send_configure).
///
/// The state will be sent to the client when calling
/// send_configure.
pub fn with_pending_state<F, T>(&self, f: F) -> Result<T, DeadResource> pub fn with_pending_state<F, T>(&self, f: F) -> Result<T, DeadResource>
where where
F: FnOnce(&mut PopupState) -> T, F: FnOnce(&mut PopupState) -> T,
@ -1515,7 +1510,7 @@ pub enum XdgRequest {
/// A new toplevel surface was created /// A new toplevel surface was created
/// ///
/// You likely need to send a [`ToplevelConfigure`] to the surface, to hint the /// You likely need to send a [`ToplevelConfigure`] to the surface, to hint the
/// client as to how its window should be /// client as to how its window should be sized.
NewToplevel { NewToplevel {
/// the surface /// the surface
surface: ToplevelSurface, surface: ToplevelSurface,
@ -1523,7 +1518,7 @@ pub enum XdgRequest {
/// A new popup surface was created /// A new popup surface was created
/// ///
/// You likely need to send a [`PopupConfigure`] to the surface, to hint the /// You likely need to send a [`PopupConfigure`] to the surface, to hint the
/// client as to how its popup should be /// client as to how its popup should be sized.
NewPopup { NewPopup {
/// the surface /// the surface
surface: PopupSurface, surface: PopupSurface,

View File

@ -7,6 +7,7 @@ use crate::wayland::Serial;
use wayland_protocols::xdg_shell::server::{ use wayland_protocols::xdg_shell::server::{
xdg_popup, xdg_positioner, xdg_surface, xdg_toplevel, xdg_wm_base, xdg_popup, xdg_positioner, xdg_surface, xdg_toplevel, xdg_wm_base,
}; };
use wayland_server::DispatchData;
use wayland_server::{protocol::wl_surface, Filter, Main}; use wayland_server::{protocol::wl_surface, Filter, Main};
use crate::utils::Rectangle; use crate::utils::Rectangle;
@ -23,16 +24,20 @@ static XDG_POPUP_ROLE: &str = "xdg_popup";
pub(crate) fn implement_wm_base( pub(crate) fn implement_wm_base(
shell: Main<xdg_wm_base::XdgWmBase>, shell: Main<xdg_wm_base::XdgWmBase>,
shell_data: &ShellData, shell_data: &ShellData,
dispatch_data: DispatchData<'_>,
) -> xdg_wm_base::XdgWmBase { ) -> xdg_wm_base::XdgWmBase {
shell.quick_assign(|shell, req, _data| wm_implementation(req, shell.deref().clone())); shell.quick_assign(wm_implementation);
shell.as_ref().user_data().set(|| ShellUserData { shell.as_ref().user_data().set(|| ShellUserData {
shell_data: shell_data.clone(), shell_data: shell_data.clone(),
client_data: Mutex::new(make_shell_client_data()), client_data: Mutex::new(make_shell_client_data()),
}); });
let mut user_impl = shell_data.user_impl.borrow_mut(); let mut user_impl = shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::NewClient { (&mut *user_impl)(
XdgRequest::NewClient {
client: make_shell_client(&shell), client: make_shell_client(&shell),
}); },
dispatch_data,
);
shell.deref().clone() shell.deref().clone()
} }
@ -51,7 +56,11 @@ pub(crate) fn make_shell_client(resource: &xdg_wm_base::XdgWmBase) -> ShellClien
} }
} }
fn wm_implementation(request: xdg_wm_base::Request, shell: xdg_wm_base::XdgWmBase) { fn wm_implementation(
shell: Main<xdg_wm_base::XdgWmBase>,
request: xdg_wm_base::Request,
dispatch_data: DispatchData<'_>,
) {
let data = shell.as_ref().user_data().get::<ShellUserData>().unwrap(); let data = shell.as_ref().user_data().get::<ShellUserData>().unwrap();
match request { match request {
xdg_wm_base::Request::Destroy => { xdg_wm_base::Request::Destroy => {
@ -64,12 +73,14 @@ fn wm_implementation(request: xdg_wm_base::Request, shell: xdg_wm_base::XdgWmBas
// Do not assign a role to the surface here // Do not assign a role to the surface here
// xdg_surface is not role, only xdg_toplevel and // xdg_surface is not role, only xdg_toplevel and
// xdg_popup are defined as roles // xdg_popup are defined as roles
id.quick_assign(|surface, req, _data| xdg_surface_implementation(req, surface.deref().clone())); id.quick_assign(|surface, req, dispatch_data| {
xdg_surface_implementation(req, surface.deref().clone(), dispatch_data)
});
id.assign_destructor(Filter::new(|surface, _, _data| destroy_surface(surface))); id.assign_destructor(Filter::new(|surface, _, _data| destroy_surface(surface)));
id.as_ref().user_data().set(|| XdgSurfaceUserData { id.as_ref().user_data().set(|| XdgSurfaceUserData {
shell_data: data.shell_data.clone(), shell_data: data.shell_data.clone(),
wl_surface: surface, wl_surface: surface,
wm_base: shell.clone(), wm_base: shell.deref().clone(),
has_active_role: AtomicBool::new(false), has_active_role: AtomicBool::new(false),
}); });
} }
@ -86,9 +97,12 @@ fn wm_implementation(request: xdg_wm_base::Request, shell: xdg_wm_base::XdgWmBas
}; };
if valid { if valid {
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::ClientPong { (&mut *user_impl)(
XdgRequest::ClientPong {
client: make_shell_client(&shell), client: make_shell_client(&shell),
}); },
dispatch_data,
);
} }
} }
_ => unreachable!(), _ => unreachable!(),
@ -191,7 +205,11 @@ fn destroy_surface(surface: xdg_surface::XdgSurface) {
} }
} }
fn xdg_surface_implementation(request: xdg_surface::Request, xdg_surface: xdg_surface::XdgSurface) { fn xdg_surface_implementation(
request: xdg_surface::Request,
xdg_surface: xdg_surface::XdgSurface,
dispatch_data: DispatchData<'_>,
) {
let data = xdg_surface let data = xdg_surface
.as_ref() .as_ref()
.user_data() .user_data()
@ -225,7 +243,7 @@ fn xdg_surface_implementation(request: xdg_surface::Request, xdg_surface: xdg_su
compositor::add_commit_hook(surface, super::ToplevelSurface::commit_hook); compositor::add_commit_hook(surface, super::ToplevelSurface::commit_hook);
id.quick_assign(|toplevel, req, _data| toplevel_implementation(req, toplevel.deref().clone())); id.quick_assign(toplevel_implementation);
id.assign_destructor(Filter::new(|toplevel, _, _data| destroy_toplevel(toplevel))); id.assign_destructor(Filter::new(|toplevel, _, _data| destroy_toplevel(toplevel)));
id.as_ref().user_data().set(|| ShellSurfaceUserData { id.as_ref().user_data().set(|| ShellSurfaceUserData {
shell_data: data.shell_data.clone(), shell_data: data.shell_data.clone(),
@ -243,7 +261,7 @@ fn xdg_surface_implementation(request: xdg_surface::Request, xdg_surface: xdg_su
let handle = make_toplevel_handle(&id); let handle = make_toplevel_handle(&id);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::NewToplevel { surface: handle }); (&mut *user_impl)(XdgRequest::NewToplevel { surface: handle }, dispatch_data);
} }
xdg_surface::Request::GetPopup { xdg_surface::Request::GetPopup {
id, id,
@ -300,7 +318,7 @@ fn xdg_surface_implementation(request: xdg_surface::Request, xdg_surface: xdg_su
compositor::add_commit_hook(surface, super::PopupSurface::commit_hook); compositor::add_commit_hook(surface, super::PopupSurface::commit_hook);
id.quick_assign(|popup, req, _data| xdg_popup_implementation(req, popup.deref().clone())); id.quick_assign(xdg_popup_implementation);
id.assign_destructor(Filter::new(|popup, _, _data| destroy_popup(popup))); id.assign_destructor(Filter::new(|popup, _, _data| destroy_popup(popup)));
id.as_ref().user_data().set(|| ShellSurfaceUserData { id.as_ref().user_data().set(|| ShellSurfaceUserData {
shell_data: data.shell_data.clone(), shell_data: data.shell_data.clone(),
@ -318,7 +336,7 @@ fn xdg_surface_implementation(request: xdg_surface::Request, xdg_surface: xdg_su
let handle = make_popup_handle(&id); let handle = make_popup_handle(&id);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::NewPopup { surface: handle }); (&mut *user_impl)(XdgRequest::NewPopup { surface: handle }, dispatch_data);
} }
xdg_surface::Request::SetWindowGeometry { x, y, width, height } => { xdg_surface::Request::SetWindowGeometry { x, y, width, height } => {
// Check the role of the surface, this can be either xdg_toplevel // Check the role of the surface, this can be either xdg_toplevel
@ -417,10 +435,13 @@ fn xdg_surface_implementation(request: xdg_surface::Request, xdg_surface: xdg_su
}; };
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::AckConfigure { (&mut *user_impl)(
XdgRequest::AckConfigure {
surface: surface.clone(), surface: surface.clone(),
configure, configure,
}); },
dispatch_data,
);
} }
_ => unreachable!(), _ => unreachable!(),
} }
@ -512,7 +533,11 @@ fn make_toplevel_handle(resource: &xdg_toplevel::XdgToplevel) -> super::Toplevel
} }
} }
fn toplevel_implementation(request: xdg_toplevel::Request, toplevel: xdg_toplevel::XdgToplevel) { fn toplevel_implementation(
toplevel: Main<xdg_toplevel::XdgToplevel>,
request: xdg_toplevel::Request,
dispatch_data: DispatchData<'_>,
) {
let data = toplevel let data = toplevel
.as_ref() .as_ref()
.user_data() .user_data()
@ -553,35 +578,44 @@ fn toplevel_implementation(request: xdg_toplevel::Request, toplevel: xdg_topleve
let handle = make_toplevel_handle(&toplevel); let handle = make_toplevel_handle(&toplevel);
let serial = Serial::from(serial); let serial = Serial::from(serial);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::ShowWindowMenu { (&mut *user_impl)(
XdgRequest::ShowWindowMenu {
surface: handle, surface: handle,
seat, seat,
serial, serial,
location: (x, y), location: (x, y),
}); },
dispatch_data,
);
} }
xdg_toplevel::Request::Move { seat, serial } => { xdg_toplevel::Request::Move { seat, serial } => {
// This has to be handled by the compositor // This has to be handled by the compositor
let handle = make_toplevel_handle(&toplevel); let handle = make_toplevel_handle(&toplevel);
let serial = Serial::from(serial); let serial = Serial::from(serial);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::Move { (&mut *user_impl)(
XdgRequest::Move {
surface: handle, surface: handle,
seat, seat,
serial, serial,
}); },
dispatch_data,
);
} }
xdg_toplevel::Request::Resize { seat, serial, edges } => { xdg_toplevel::Request::Resize { seat, serial, edges } => {
// This has to be handled by the compositor // This has to be handled by the compositor
let handle = make_toplevel_handle(&toplevel); let handle = make_toplevel_handle(&toplevel);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
let serial = Serial::from(serial); let serial = Serial::from(serial);
(&mut *user_impl)(XdgRequest::Resize { (&mut *user_impl)(
XdgRequest::Resize {
surface: handle, surface: handle,
seat, seat,
serial, serial,
edges, edges,
}); },
dispatch_data,
);
} }
xdg_toplevel::Request::SetMaxSize { width, height } => { xdg_toplevel::Request::SetMaxSize { width, height } => {
with_toplevel_pending_state(&toplevel, |toplevel_data| { with_toplevel_pending_state(&toplevel, |toplevel_data| {
@ -596,32 +630,35 @@ fn toplevel_implementation(request: xdg_toplevel::Request, toplevel: xdg_topleve
xdg_toplevel::Request::SetMaximized => { xdg_toplevel::Request::SetMaximized => {
let handle = make_toplevel_handle(&toplevel); let handle = make_toplevel_handle(&toplevel);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::Maximize { surface: handle }); (&mut *user_impl)(XdgRequest::Maximize { surface: handle }, dispatch_data);
} }
xdg_toplevel::Request::UnsetMaximized => { xdg_toplevel::Request::UnsetMaximized => {
let handle = make_toplevel_handle(&toplevel); let handle = make_toplevel_handle(&toplevel);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::UnMaximize { surface: handle }); (&mut *user_impl)(XdgRequest::UnMaximize { surface: handle }, dispatch_data);
} }
xdg_toplevel::Request::SetFullscreen { output } => { xdg_toplevel::Request::SetFullscreen { output } => {
let handle = make_toplevel_handle(&toplevel); let handle = make_toplevel_handle(&toplevel);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::Fullscreen { (&mut *user_impl)(
XdgRequest::Fullscreen {
surface: handle, surface: handle,
output, output,
}); },
dispatch_data,
);
} }
xdg_toplevel::Request::UnsetFullscreen => { xdg_toplevel::Request::UnsetFullscreen => {
let handle = make_toplevel_handle(&toplevel); let handle = make_toplevel_handle(&toplevel);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::UnFullscreen { surface: handle }); (&mut *user_impl)(XdgRequest::UnFullscreen { surface: handle }, dispatch_data);
} }
xdg_toplevel::Request::SetMinimized => { xdg_toplevel::Request::SetMinimized => {
// This has to be handled by the compositor, may not be // This has to be handled by the compositor, may not be
// supported and just ignored // supported and just ignored
let handle = make_toplevel_handle(&toplevel); let handle = make_toplevel_handle(&toplevel);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::Minimize { surface: handle }); (&mut *user_impl)(XdgRequest::Minimize { surface: handle }, dispatch_data);
} }
_ => unreachable!(), _ => unreachable!(),
} }
@ -679,7 +716,11 @@ fn make_popup_handle(resource: &xdg_popup::XdgPopup) -> super::PopupSurface {
} }
} }
fn xdg_popup_implementation(request: xdg_popup::Request, popup: xdg_popup::XdgPopup) { fn xdg_popup_implementation(
popup: Main<xdg_popup::XdgPopup>,
request: xdg_popup::Request,
dispatch_data: DispatchData<'_>,
) {
let data = popup.as_ref().user_data().get::<ShellSurfaceUserData>().unwrap(); let data = popup.as_ref().user_data().get::<ShellSurfaceUserData>().unwrap();
match request { match request {
xdg_popup::Request::Destroy => { xdg_popup::Request::Destroy => {
@ -689,11 +730,14 @@ fn xdg_popup_implementation(request: xdg_popup::Request, popup: xdg_popup::XdgPo
let handle = make_popup_handle(&popup); let handle = make_popup_handle(&popup);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
let serial = Serial::from(serial); let serial = Serial::from(serial);
(&mut *user_impl)(XdgRequest::Grab { (&mut *user_impl)(
XdgRequest::Grab {
surface: handle, surface: handle,
seat, seat,
serial, serial,
}); },
dispatch_data,
);
} }
_ => unreachable!(), _ => unreachable!(),
} }

View File

@ -10,6 +10,7 @@ use wayland_protocols::{
}, },
xdg_shell::server::{xdg_positioner, xdg_toplevel}, xdg_shell::server::{xdg_positioner, xdg_toplevel},
}; };
use wayland_server::DispatchData;
use wayland_server::{protocol::wl_surface, Filter, Main}; use wayland_server::{protocol::wl_surface, Filter, Main};
use crate::utils::Rectangle; use crate::utils::Rectangle;
@ -26,16 +27,20 @@ static ZXDG_POPUP_ROLE: &str = "zxdg_toplevel";
pub(crate) fn implement_shell( pub(crate) fn implement_shell(
shell: Main<zxdg_shell_v6::ZxdgShellV6>, shell: Main<zxdg_shell_v6::ZxdgShellV6>,
shell_data: &ShellData, shell_data: &ShellData,
dispatch_data: DispatchData<'_>,
) -> zxdg_shell_v6::ZxdgShellV6 { ) -> zxdg_shell_v6::ZxdgShellV6 {
shell.quick_assign(|shell, req, _data| shell_implementation(req, shell.deref().clone())); shell.quick_assign(shell_implementation);
shell.as_ref().user_data().set(|| ShellUserData { shell.as_ref().user_data().set(|| ShellUserData {
shell_data: shell_data.clone(), shell_data: shell_data.clone(),
client_data: Mutex::new(make_shell_client_data()), client_data: Mutex::new(make_shell_client_data()),
}); });
let mut user_impl = shell_data.user_impl.borrow_mut(); let mut user_impl = shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::NewClient { (&mut *user_impl)(
XdgRequest::NewClient {
client: make_shell_client(&shell), client: make_shell_client(&shell),
}); },
dispatch_data,
);
shell.deref().clone() shell.deref().clone()
} }
@ -54,7 +59,11 @@ pub(crate) fn make_shell_client(resource: &zxdg_shell_v6::ZxdgShellV6) -> ShellC
} }
} }
fn shell_implementation(request: zxdg_shell_v6::Request, shell: zxdg_shell_v6::ZxdgShellV6) { fn shell_implementation(
shell: Main<zxdg_shell_v6::ZxdgShellV6>,
request: zxdg_shell_v6::Request,
dispatch_data: DispatchData<'_>,
) {
let data = shell.as_ref().user_data().get::<ShellUserData>().unwrap(); let data = shell.as_ref().user_data().get::<ShellUserData>().unwrap();
match request { match request {
zxdg_shell_v6::Request::Destroy => { zxdg_shell_v6::Request::Destroy => {
@ -64,12 +73,12 @@ fn shell_implementation(request: zxdg_shell_v6::Request, shell: zxdg_shell_v6::Z
implement_positioner(id); implement_positioner(id);
} }
zxdg_shell_v6::Request::GetXdgSurface { id, surface } => { zxdg_shell_v6::Request::GetXdgSurface { id, surface } => {
id.quick_assign(|surface, req, _data| xdg_surface_implementation(req, surface.deref().clone())); id.quick_assign(xdg_surface_implementation);
id.assign_destructor(Filter::new(|surface, _, _data| destroy_surface(surface))); id.assign_destructor(Filter::new(|surface, _, _data| destroy_surface(surface)));
id.as_ref().user_data().set(|| XdgSurfaceUserData { id.as_ref().user_data().set(|| XdgSurfaceUserData {
shell_data: data.shell_data.clone(), shell_data: data.shell_data.clone(),
wl_surface: surface, wl_surface: surface,
shell: shell.clone(), shell: shell.deref().clone(),
has_active_role: AtomicBool::new(false), has_active_role: AtomicBool::new(false),
}); });
} }
@ -86,9 +95,12 @@ fn shell_implementation(request: zxdg_shell_v6::Request, shell: zxdg_shell_v6::Z
}; };
if valid { if valid {
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::ClientPong { (&mut *user_impl)(
XdgRequest::ClientPong {
client: make_shell_client(&shell), client: make_shell_client(&shell),
}); },
dispatch_data,
);
} }
} }
_ => unreachable!(), _ => unreachable!(),
@ -208,8 +220,9 @@ fn destroy_surface(surface: zxdg_surface_v6::ZxdgSurfaceV6) {
} }
fn xdg_surface_implementation( fn xdg_surface_implementation(
xdg_surface: Main<zxdg_surface_v6::ZxdgSurfaceV6>,
request: zxdg_surface_v6::Request, request: zxdg_surface_v6::Request,
xdg_surface: zxdg_surface_v6::ZxdgSurfaceV6, dispatch_data: DispatchData<'_>,
) { ) {
let data = xdg_surface let data = xdg_surface
.as_ref() .as_ref()
@ -244,13 +257,13 @@ fn xdg_surface_implementation(
compositor::add_commit_hook(surface, super::ToplevelSurface::commit_hook); compositor::add_commit_hook(surface, super::ToplevelSurface::commit_hook);
id.quick_assign(|toplevel, req, _data| toplevel_implementation(req, toplevel.deref().clone())); id.quick_assign(toplevel_implementation);
id.assign_destructor(Filter::new(|toplevel, _, _data| destroy_toplevel(toplevel))); id.assign_destructor(Filter::new(|toplevel, _, _data| destroy_toplevel(toplevel)));
id.as_ref().user_data().set(|| ShellSurfaceUserData { id.as_ref().user_data().set(|| ShellSurfaceUserData {
shell_data: data.shell_data.clone(), shell_data: data.shell_data.clone(),
wl_surface: data.wl_surface.clone(), wl_surface: data.wl_surface.clone(),
shell: data.shell.clone(), shell: data.shell.clone(),
xdg_surface: xdg_surface.clone(), xdg_surface: xdg_surface.deref().clone(),
}); });
data.shell_data data.shell_data
@ -262,7 +275,7 @@ fn xdg_surface_implementation(
let handle = make_toplevel_handle(&id); let handle = make_toplevel_handle(&id);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::NewToplevel { surface: handle }); (&mut *user_impl)(XdgRequest::NewToplevel { surface: handle }, dispatch_data);
} }
zxdg_surface_v6::Request::GetPopup { zxdg_surface_v6::Request::GetPopup {
id, id,
@ -319,13 +332,13 @@ fn xdg_surface_implementation(
compositor::add_commit_hook(surface, super::PopupSurface::commit_hook); compositor::add_commit_hook(surface, super::PopupSurface::commit_hook);
id.quick_assign(|popup, req, _data| popup_implementation(req, popup.deref().clone())); id.quick_assign(popup_implementation);
id.assign_destructor(Filter::new(|popup, _, _data| destroy_popup(popup))); id.assign_destructor(Filter::new(|popup, _, _data| destroy_popup(popup)));
id.as_ref().user_data().set(|| ShellSurfaceUserData { id.as_ref().user_data().set(|| ShellSurfaceUserData {
shell_data: data.shell_data.clone(), shell_data: data.shell_data.clone(),
wl_surface: data.wl_surface.clone(), wl_surface: data.wl_surface.clone(),
shell: data.shell.clone(), shell: data.shell.clone(),
xdg_surface: xdg_surface.clone(), xdg_surface: xdg_surface.deref().clone(),
}); });
data.shell_data data.shell_data
@ -337,7 +350,7 @@ fn xdg_surface_implementation(
let handle = make_popup_handle(&id); let handle = make_popup_handle(&id);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::NewPopup { surface: handle }); (&mut *user_impl)(XdgRequest::NewPopup { surface: handle }, dispatch_data);
} }
zxdg_surface_v6::Request::SetWindowGeometry { x, y, width, height } => { zxdg_surface_v6::Request::SetWindowGeometry { x, y, width, height } => {
// Check the role of the surface, this can be either xdg_toplevel // Check the role of the surface, this can be either xdg_toplevel
@ -437,10 +450,13 @@ fn xdg_surface_implementation(
}; };
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::AckConfigure { (&mut *user_impl)(
XdgRequest::AckConfigure {
surface: surface.clone(), surface: surface.clone(),
configure, configure,
}); },
dispatch_data,
);
} }
_ => unreachable!(), _ => unreachable!(),
} }
@ -532,7 +548,11 @@ fn make_toplevel_handle(resource: &zxdg_toplevel_v6::ZxdgToplevelV6) -> super::T
} }
} }
fn toplevel_implementation(request: zxdg_toplevel_v6::Request, toplevel: zxdg_toplevel_v6::ZxdgToplevelV6) { fn toplevel_implementation(
toplevel: Main<zxdg_toplevel_v6::ZxdgToplevelV6>,
request: zxdg_toplevel_v6::Request,
dispatch_data: DispatchData<'_>,
) {
let data = toplevel let data = toplevel
.as_ref() .as_ref()
.user_data() .user_data()
@ -570,22 +590,28 @@ fn toplevel_implementation(request: zxdg_toplevel_v6::Request, toplevel: zxdg_to
let handle = make_toplevel_handle(&toplevel); let handle = make_toplevel_handle(&toplevel);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
let serial = Serial::from(serial); let serial = Serial::from(serial);
(&mut *user_impl)(XdgRequest::ShowWindowMenu { (&mut *user_impl)(
XdgRequest::ShowWindowMenu {
surface: handle, surface: handle,
seat, seat,
serial, serial,
location: (x, y), location: (x, y),
}); },
dispatch_data,
);
} }
zxdg_toplevel_v6::Request::Move { seat, serial } => { zxdg_toplevel_v6::Request::Move { seat, serial } => {
let handle = make_toplevel_handle(&toplevel); let handle = make_toplevel_handle(&toplevel);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
let serial = Serial::from(serial); let serial = Serial::from(serial);
(&mut *user_impl)(XdgRequest::Move { (&mut *user_impl)(
XdgRequest::Move {
surface: handle, surface: handle,
seat, seat,
serial, serial,
}); },
dispatch_data,
);
} }
zxdg_toplevel_v6::Request::Resize { seat, serial, edges } => { zxdg_toplevel_v6::Request::Resize { seat, serial, edges } => {
let edges = let edges =
@ -593,12 +619,15 @@ fn toplevel_implementation(request: zxdg_toplevel_v6::Request, toplevel: zxdg_to
let handle = make_toplevel_handle(&toplevel); let handle = make_toplevel_handle(&toplevel);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
let serial = Serial::from(serial); let serial = Serial::from(serial);
(&mut *user_impl)(XdgRequest::Resize { (&mut *user_impl)(
XdgRequest::Resize {
surface: handle, surface: handle,
seat, seat,
serial, serial,
edges: zxdg_edges_to_xdg(edges), edges: zxdg_edges_to_xdg(edges),
}); },
dispatch_data,
);
} }
zxdg_toplevel_v6::Request::SetMaxSize { width, height } => { zxdg_toplevel_v6::Request::SetMaxSize { width, height } => {
with_toplevel_pending_state(&toplevel, |toplevel_data| { with_toplevel_pending_state(&toplevel, |toplevel_data| {
@ -613,32 +642,35 @@ fn toplevel_implementation(request: zxdg_toplevel_v6::Request, toplevel: zxdg_to
zxdg_toplevel_v6::Request::SetMaximized => { zxdg_toplevel_v6::Request::SetMaximized => {
let handle = make_toplevel_handle(&toplevel); let handle = make_toplevel_handle(&toplevel);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::Maximize { surface: handle }); (&mut *user_impl)(XdgRequest::Maximize { surface: handle }, dispatch_data);
} }
zxdg_toplevel_v6::Request::UnsetMaximized => { zxdg_toplevel_v6::Request::UnsetMaximized => {
let handle = make_toplevel_handle(&toplevel); let handle = make_toplevel_handle(&toplevel);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::UnMaximize { surface: handle }); (&mut *user_impl)(XdgRequest::UnMaximize { surface: handle }, dispatch_data);
} }
zxdg_toplevel_v6::Request::SetFullscreen { output } => { zxdg_toplevel_v6::Request::SetFullscreen { output } => {
let handle = make_toplevel_handle(&toplevel); let handle = make_toplevel_handle(&toplevel);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::Fullscreen { (&mut *user_impl)(
XdgRequest::Fullscreen {
surface: handle, surface: handle,
output, output,
}); },
dispatch_data,
);
} }
zxdg_toplevel_v6::Request::UnsetFullscreen => { zxdg_toplevel_v6::Request::UnsetFullscreen => {
let handle = make_toplevel_handle(&toplevel); let handle = make_toplevel_handle(&toplevel);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::UnFullscreen { surface: handle }); (&mut *user_impl)(XdgRequest::UnFullscreen { surface: handle }, dispatch_data);
} }
zxdg_toplevel_v6::Request::SetMinimized => { zxdg_toplevel_v6::Request::SetMinimized => {
// This has to be handled by the compositor, may not be // This has to be handled by the compositor, may not be
// supported and just ignored // supported and just ignored
let handle = make_toplevel_handle(&toplevel); let handle = make_toplevel_handle(&toplevel);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
(&mut *user_impl)(XdgRequest::Minimize { surface: handle }); (&mut *user_impl)(XdgRequest::Minimize { surface: handle }, dispatch_data);
} }
_ => unreachable!(), _ => unreachable!(),
} }
@ -696,7 +728,11 @@ fn make_popup_handle(resource: &zxdg_popup_v6::ZxdgPopupV6) -> super::PopupSurfa
} }
} }
fn popup_implementation(request: zxdg_popup_v6::Request, popup: zxdg_popup_v6::ZxdgPopupV6) { fn popup_implementation(
popup: Main<zxdg_popup_v6::ZxdgPopupV6>,
request: zxdg_popup_v6::Request,
dispatch_data: DispatchData<'_>,
) {
let data = popup.as_ref().user_data().get::<ShellSurfaceUserData>().unwrap(); let data = popup.as_ref().user_data().get::<ShellSurfaceUserData>().unwrap();
match request { match request {
zxdg_popup_v6::Request::Destroy => { zxdg_popup_v6::Request::Destroy => {
@ -706,11 +742,14 @@ fn popup_implementation(request: zxdg_popup_v6::Request, popup: zxdg_popup_v6::Z
let handle = make_popup_handle(&popup); let handle = make_popup_handle(&popup);
let mut user_impl = data.shell_data.user_impl.borrow_mut(); let mut user_impl = data.shell_data.user_impl.borrow_mut();
let serial = Serial::from(serial); let serial = Serial::from(serial);
(&mut *user_impl)(XdgRequest::Grab { (&mut *user_impl)(
XdgRequest::Grab {
surface: handle, surface: handle,
seat, seat,
serial, serial,
}); },
dispatch_data,
);
} }
_ => unreachable!(), _ => unreachable!(),
} }