rework xdg_shell (#286)
* rework xdg_shell use distinct surface roles for xdg_toplevel and xdg_popup using a xdg_role! macro * fix clippy warnings in shell * added a generic DeadResource error and... ...added a result to xdg with_pending_state Renamed the ToplevelState to ToplevelStateSet
This commit is contained in:
parent
41c7b22cc4
commit
e9aef7caad
|
@ -28,8 +28,8 @@ use smithay::{
|
||||||
wl_shell_init, ShellRequest, ShellState as WlShellState, ShellSurfaceKind, ShellSurfaceRole,
|
wl_shell_init, ShellRequest, ShellState as WlShellState, ShellSurfaceKind, ShellSurfaceRole,
|
||||||
},
|
},
|
||||||
xdg::{
|
xdg::{
|
||||||
xdg_shell_init, PopupConfigure, ShellState as XdgShellState, ToplevelConfigure, XdgRequest,
|
xdg_shell_init, Configure, ShellState as XdgShellState, XdgPopupSurfaceRole, XdgRequest,
|
||||||
XdgSurfacePendingState, XdgSurfaceRole,
|
XdgToplevelSurfaceRole,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Serial,
|
Serial,
|
||||||
|
@ -38,24 +38,17 @@ use smithay::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
state::AnvilState,
|
state::AnvilState,
|
||||||
window_map::{Kind as SurfaceKind, WindowMap},
|
window_map::{Kind as SurfaceKind, PopupKind, WindowMap},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "xwayland")]
|
#[cfg(feature = "xwayland")]
|
||||||
use crate::xwayland::X11SurfaceRole;
|
use crate::xwayland::X11SurfaceRole;
|
||||||
|
|
||||||
// The xwayland feature only adds a X11Surface role, but the macro does not support #[cfg]
|
|
||||||
#[cfg(not(feature = "xwayland"))]
|
|
||||||
define_roles!(Roles =>
|
define_roles!(Roles =>
|
||||||
[ XdgSurface, XdgSurfaceRole ]
|
[ XdgToplevelSurface, XdgToplevelSurfaceRole ]
|
||||||
[ ShellSurface, ShellSurfaceRole]
|
[ XdgPopupSurface, XdgPopupSurfaceRole ]
|
||||||
[ DnDIcon, DnDIconRole ]
|
|
||||||
[ CursorImage, CursorImageRole ]
|
|
||||||
);
|
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
define_roles!(Roles =>
|
|
||||||
[ XdgSurface, XdgSurfaceRole ]
|
|
||||||
[ ShellSurface, ShellSurfaceRole]
|
[ ShellSurface, ShellSurfaceRole]
|
||||||
|
#[cfg(feature = "xwayland")]
|
||||||
[ X11Surface, X11SurfaceRole ]
|
[ X11Surface, X11SurfaceRole ]
|
||||||
[ DnDIcon, DnDIconRole ]
|
[ DnDIcon, DnDIconRole ]
|
||||||
[ CursorImage, CursorImageRole ]
|
[ CursorImage, CursorImageRole ]
|
||||||
|
@ -172,7 +165,7 @@ impl PointerGrab for ResizeSurfaceGrab {
|
||||||
_handle: &mut PointerInnerHandle<'_>,
|
_handle: &mut PointerInnerHandle<'_>,
|
||||||
location: (f64, f64),
|
location: (f64, f64),
|
||||||
_focus: Option<(wl_surface::WlSurface, (f64, f64))>,
|
_focus: Option<(wl_surface::WlSurface, (f64, f64))>,
|
||||||
serial: Serial,
|
_serial: Serial,
|
||||||
_time: u32,
|
_time: u32,
|
||||||
) {
|
) {
|
||||||
let mut dx = location.0 - self.start_data.location.0;
|
let mut dx = location.0 - self.start_data.location.0;
|
||||||
|
@ -226,11 +219,17 @@ impl PointerGrab for ResizeSurfaceGrab {
|
||||||
self.last_window_size = (new_window_width, new_window_height);
|
self.last_window_size = (new_window_width, new_window_height);
|
||||||
|
|
||||||
match &self.toplevel {
|
match &self.toplevel {
|
||||||
SurfaceKind::Xdg(xdg) => xdg.send_configure(ToplevelConfigure {
|
SurfaceKind::Xdg(xdg) => {
|
||||||
size: Some(self.last_window_size),
|
if xdg
|
||||||
states: vec![xdg_toplevel::State::Resizing],
|
.with_pending_state(|state| {
|
||||||
serial,
|
state.states.set(xdg_toplevel::State::Resizing);
|
||||||
}),
|
state.size = Some(self.last_window_size);
|
||||||
|
})
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
xdg.send_configure();
|
||||||
|
}
|
||||||
|
}
|
||||||
SurfaceKind::Wl(wl) => wl.send_configure(
|
SurfaceKind::Wl(wl) => wl.send_configure(
|
||||||
(self.last_window_size.0 as u32, self.last_window_size.1 as u32),
|
(self.last_window_size.0 as u32, self.last_window_size.1 as u32),
|
||||||
self.edges.into(),
|
self.edges.into(),
|
||||||
|
@ -256,12 +255,15 @@ impl PointerGrab for ResizeSurfaceGrab {
|
||||||
handle.unset_grab(serial, time);
|
handle.unset_grab(serial, time);
|
||||||
|
|
||||||
if let SurfaceKind::Xdg(xdg) = &self.toplevel {
|
if let SurfaceKind::Xdg(xdg) = &self.toplevel {
|
||||||
// Send the final configure without the resizing state.
|
if xdg
|
||||||
xdg.send_configure(ToplevelConfigure {
|
.with_pending_state(|state| {
|
||||||
size: Some(self.last_window_size),
|
state.states.unset(xdg_toplevel::State::Resizing);
|
||||||
states: vec![],
|
state.size = Some(self.last_window_size);
|
||||||
serial,
|
})
|
||||||
});
|
.is_ok()
|
||||||
|
{
|
||||||
|
xdg.send_configure();
|
||||||
|
}
|
||||||
|
|
||||||
self.ctoken
|
self.ctoken
|
||||||
.with_surface_data(self.toplevel.get_surface().unwrap(), |attrs| {
|
.with_surface_data(self.toplevel.get_surface().unwrap(), |attrs| {
|
||||||
|
@ -348,20 +350,19 @@ pub fn init_shell<Backend: 'static>(display: &mut Display, log: ::slog::Logger)
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let x = range.sample(&mut rng);
|
let x = range.sample(&mut rng);
|
||||||
let y = range.sample(&mut rng);
|
let y = range.sample(&mut rng);
|
||||||
surface.send_configure(ToplevelConfigure {
|
// Do not send a configure here, the initial configure
|
||||||
size: None,
|
// of a xdg_surface has to be sent during the commit if
|
||||||
states: vec![],
|
// the surface is not already configured
|
||||||
serial: Serial::from(42),
|
|
||||||
});
|
|
||||||
xdg_window_map
|
xdg_window_map
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(SurfaceKind::Xdg(surface), (x, y));
|
.insert(SurfaceKind::Xdg(surface), (x, y));
|
||||||
}
|
}
|
||||||
XdgRequest::NewPopup { surface } => surface.send_configure(PopupConfigure {
|
XdgRequest::NewPopup { surface } => {
|
||||||
size: (10, 10),
|
// Do not send a configure here, the initial configure
|
||||||
position: (10, 10),
|
// of a xdg_surface has to be sent during the commit if
|
||||||
serial: Serial::from(42),
|
// the surface is not already configured
|
||||||
}),
|
xdg_window_map.borrow_mut().insert_popup(PopupKind::Xdg(surface));
|
||||||
|
}
|
||||||
XdgRequest::Move {
|
XdgRequest::Move {
|
||||||
surface,
|
surface,
|
||||||
seat,
|
seat,
|
||||||
|
@ -462,38 +463,95 @@ pub fn init_shell<Backend: 'static>(display: &mut Display, log: ::slog::Logger)
|
||||||
|
|
||||||
pointer.set_grab(grab, serial);
|
pointer.set_grab(grab, serial);
|
||||||
}
|
}
|
||||||
XdgRequest::AckConfigure { surface, .. } => {
|
XdgRequest::AckConfigure {
|
||||||
let waiting_for_serial = compositor_token.with_surface_data(&surface, |attrs| {
|
surface, configure, ..
|
||||||
if let Some(data) = attrs.user_data.get::<RefCell<SurfaceData>>() {
|
} => {
|
||||||
if let ResizeState::WaitingForFinalAck(_, serial) = data.borrow().resize_state {
|
if let Configure::Toplevel(configure) = configure {
|
||||||
return Some(serial);
|
let waiting_for_serial = compositor_token.with_surface_data(&surface, |attrs| {
|
||||||
|
if let Some(data) = attrs.user_data.get::<RefCell<SurfaceData>>() {
|
||||||
|
if let ResizeState::WaitingForFinalAck(_, serial) = data.borrow().resize_state {
|
||||||
|
return Some(serial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(serial) = waiting_for_serial {
|
||||||
|
if configure.serial > serial {
|
||||||
|
// TODO: huh, we have missed the serial somehow.
|
||||||
|
// this should not happen, but it may be better to handle
|
||||||
|
// this case anyway
|
||||||
|
}
|
||||||
|
|
||||||
|
if serial == configure.serial {
|
||||||
|
if configure.state.states.contains(xdg_toplevel::State::Resizing) {
|
||||||
|
compositor_token.with_surface_data(&surface, |attrs| {
|
||||||
|
let mut data = attrs
|
||||||
|
.user_data
|
||||||
|
.get::<RefCell<SurfaceData>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow_mut();
|
||||||
|
if let ResizeState::WaitingForFinalAck(resize_data, _) = data.resize_state
|
||||||
|
{
|
||||||
|
data.resize_state = ResizeState::WaitingForCommit(resize_data);
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
None
|
}
|
||||||
});
|
XdgRequest::Fullscreen { surface, output, .. } => {
|
||||||
|
if surface
|
||||||
if let Some(serial) = waiting_for_serial {
|
.with_pending_state(|state| {
|
||||||
let acked = compositor_token
|
// TODO: Use size of current output the window is on and set position to (0,0)
|
||||||
.with_role_data(&surface, |role: &mut XdgSurfaceRole| {
|
state.states.set(xdg_toplevel::State::Fullscreen);
|
||||||
!role.pending_configures.contains(&serial.into())
|
state.size = Some((800, 600));
|
||||||
})
|
// TODO: If the provided output is None, use the output where
|
||||||
.unwrap();
|
// the toplevel is currently shown
|
||||||
|
state.fullscreen_output = output;
|
||||||
if acked {
|
})
|
||||||
compositor_token.with_surface_data(&surface, |attrs| {
|
.is_ok()
|
||||||
let mut data = attrs
|
{
|
||||||
.user_data
|
surface.send_configure();
|
||||||
.get::<RefCell<SurfaceData>>()
|
}
|
||||||
.unwrap()
|
}
|
||||||
.borrow_mut();
|
XdgRequest::UnFullscreen { surface } => {
|
||||||
if let ResizeState::WaitingForFinalAck(resize_data, _) = data.resize_state {
|
if surface
|
||||||
data.resize_state = ResizeState::WaitingForCommit(resize_data);
|
.with_pending_state(|state| {
|
||||||
} else {
|
state.states.unset(xdg_toplevel::State::Fullscreen);
|
||||||
unreachable!()
|
state.size = None;
|
||||||
}
|
state.fullscreen_output = None;
|
||||||
})
|
})
|
||||||
}
|
.is_ok()
|
||||||
|
{
|
||||||
|
surface.send_configure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XdgRequest::Maximize { surface } => {
|
||||||
|
if surface
|
||||||
|
.with_pending_state(|state| {
|
||||||
|
// TODO: Use size of current output the window is on and set position to (0,0)
|
||||||
|
state.states.set(xdg_toplevel::State::Maximized);
|
||||||
|
state.size = Some((800, 600));
|
||||||
|
})
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
surface.send_configure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XdgRequest::UnMaximize { surface } => {
|
||||||
|
if surface
|
||||||
|
.with_pending_state(|state| {
|
||||||
|
state.states.unset(xdg_toplevel::State::Maximized);
|
||||||
|
state.size = None;
|
||||||
|
})
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
surface.send_configure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
@ -790,14 +848,36 @@ fn surface_commit(
|
||||||
let mut geometry = None;
|
let mut geometry = None;
|
||||||
let mut min_size = (0, 0);
|
let mut min_size = (0, 0);
|
||||||
let mut max_size = (0, 0);
|
let mut max_size = (0, 0);
|
||||||
let _ = token.with_role_data(surface, |role: &mut XdgSurfaceRole| {
|
|
||||||
if let XdgSurfacePendingState::Toplevel(ref state) = role.pending_state {
|
if token.has_role::<XdgToplevelSurfaceRole>(surface) {
|
||||||
min_size = state.min_size;
|
if let Some(SurfaceKind::Xdg(xdg)) = window_map.borrow().find(surface) {
|
||||||
max_size = state.max_size;
|
xdg.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
geometry = role.window_geometry;
|
token
|
||||||
});
|
.with_role_data(surface, |role: &mut XdgToplevelSurfaceRole| {
|
||||||
|
if let XdgToplevelSurfaceRole::Some(attributes) = role {
|
||||||
|
geometry = attributes.window_geometry;
|
||||||
|
min_size = attributes.min_size;
|
||||||
|
max_size = attributes.max_size;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if token.has_role::<XdgPopupSurfaceRole>(surface) {
|
||||||
|
if let Some(PopupKind::Xdg(xdg)) = window_map.borrow().find_popup(&surface) {
|
||||||
|
xdg.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
token
|
||||||
|
.with_role_data(surface, |role: &mut XdgPopupSurfaceRole| {
|
||||||
|
if let XdgPopupSurfaceRole::Some(attributes) = role {
|
||||||
|
geometry = attributes.window_geometry;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
let sub_data = token
|
let sub_data = token
|
||||||
.with_role_data(surface, |&mut role: &mut SubsurfaceRole| role)
|
.with_role_data(surface, |&mut role: &mut SubsurfaceRole| role)
|
||||||
|
|
|
@ -7,7 +7,7 @@ use smithay::{
|
||||||
compositor::{roles::Role, CompositorToken, SubsurfaceRole, TraversalAction},
|
compositor::{roles::Role, CompositorToken, SubsurfaceRole, TraversalAction},
|
||||||
shell::{
|
shell::{
|
||||||
legacy::{ShellSurface, ShellSurfaceRole},
|
legacy::{ShellSurface, ShellSurfaceRole},
|
||||||
xdg::{ToplevelSurface, XdgSurfaceRole},
|
xdg::{PopupSurface, ToplevelSurface, XdgPopupSurfaceRole, XdgToplevelSurfaceRole},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -37,7 +37,7 @@ impl<R> Clone for Kind<R> {
|
||||||
|
|
||||||
impl<R> Kind<R>
|
impl<R> Kind<R>
|
||||||
where
|
where
|
||||||
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole> + 'static,
|
R: Role<SubsurfaceRole> + Role<XdgToplevelSurfaceRole> + Role<ShellSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
pub fn alive(&self) -> bool {
|
pub fn alive(&self) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
|
@ -68,6 +68,35 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum PopupKind<R> {
|
||||||
|
Xdg(PopupSurface<R>),
|
||||||
|
}
|
||||||
|
|
||||||
|
// We implement Clone manually because #[derive(..)] would require R: Clone.
|
||||||
|
impl<R> Clone for PopupKind<R> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match self {
|
||||||
|
PopupKind::Xdg(xdg) => PopupKind::Xdg(xdg.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> PopupKind<R>
|
||||||
|
where
|
||||||
|
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||||
|
{
|
||||||
|
pub fn alive(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
PopupKind::Xdg(ref t) => t.alive(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> {
|
||||||
|
match *self {
|
||||||
|
PopupKind::Xdg(ref t) => t.get_surface(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Window<R> {
|
struct Window<R> {
|
||||||
location: (i32, i32),
|
location: (i32, i32),
|
||||||
/// A bounding box over this window and its children.
|
/// A bounding box over this window and its children.
|
||||||
|
@ -80,7 +109,7 @@ struct Window<R> {
|
||||||
|
|
||||||
impl<R> Window<R>
|
impl<R> Window<R>
|
||||||
where
|
where
|
||||||
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole> + 'static,
|
R: Role<SubsurfaceRole> + Role<XdgToplevelSurfaceRole> + Role<ShellSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
/// Finds the topmost surface under this point if any and returns it together with the location of this
|
/// Finds the topmost surface under this point if any and returns it together with the location of this
|
||||||
/// surface.
|
/// surface.
|
||||||
|
@ -203,19 +232,29 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Popup<R> {
|
||||||
|
popup: PopupKind<R>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct WindowMap<R> {
|
pub struct WindowMap<R> {
|
||||||
ctoken: CompositorToken<R>,
|
ctoken: CompositorToken<R>,
|
||||||
windows: Vec<Window<R>>,
|
windows: Vec<Window<R>>,
|
||||||
|
popups: Vec<Popup<R>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> WindowMap<R>
|
impl<R> WindowMap<R>
|
||||||
where
|
where
|
||||||
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole> + 'static,
|
R: Role<SubsurfaceRole>
|
||||||
|
+ Role<XdgToplevelSurfaceRole>
|
||||||
|
+ Role<XdgPopupSurfaceRole>
|
||||||
|
+ Role<ShellSurfaceRole>
|
||||||
|
+ 'static,
|
||||||
{
|
{
|
||||||
pub fn new(ctoken: CompositorToken<R>) -> Self {
|
pub fn new(ctoken: CompositorToken<R>) -> Self {
|
||||||
WindowMap {
|
WindowMap {
|
||||||
ctoken,
|
ctoken,
|
||||||
windows: Vec::new(),
|
windows: Vec::new(),
|
||||||
|
popups: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,6 +268,11 @@ where
|
||||||
self.windows.insert(0, window);
|
self.windows.insert(0, window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn insert_popup(&mut self, popup: PopupKind<R>) {
|
||||||
|
let popup = Popup { popup };
|
||||||
|
self.popups.push(popup);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_surface_under(&self, point: (f64, f64)) -> Option<(wl_surface::WlSurface, (f64, f64))> {
|
pub fn get_surface_under(&self, point: (f64, f64)) -> Option<(wl_surface::WlSurface, (f64, f64))> {
|
||||||
for w in &self.windows {
|
for w in &self.windows {
|
||||||
if let Some(surface) = w.matching(point, self.ctoken) {
|
if let Some(surface) = w.matching(point, self.ctoken) {
|
||||||
|
@ -269,6 +313,7 @@ where
|
||||||
|
|
||||||
pub fn refresh(&mut self) {
|
pub fn refresh(&mut self) {
|
||||||
self.windows.retain(|w| w.toplevel.alive());
|
self.windows.retain(|w| w.toplevel.alive());
|
||||||
|
self.popups.retain(|p| p.popup.alive());
|
||||||
for w in &mut self.windows {
|
for w in &mut self.windows {
|
||||||
w.self_update(self.ctoken);
|
w.self_update(self.ctoken);
|
||||||
}
|
}
|
||||||
|
@ -300,6 +345,20 @@ where
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find_popup(&self, surface: &wl_surface::WlSurface) -> Option<PopupKind<R>> {
|
||||||
|
self.popups.iter().find_map(|p| {
|
||||||
|
if p.popup
|
||||||
|
.get_surface()
|
||||||
|
.map(|s| s.as_ref().equals(surface.as_ref()))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
Some(p.popup.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the location of the toplevel, if it exists.
|
/// Returns the location of the toplevel, if it exists.
|
||||||
pub fn location(&self, toplevel: &Kind<R>) -> Option<(i32, i32)> {
|
pub fn location(&self, toplevel: &Kind<R>) -> Option<(i32, i32)> {
|
||||||
self.windows
|
self.windows
|
||||||
|
|
|
@ -15,3 +15,15 @@ impl std::fmt::Display for UnmanagedResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for UnmanagedResource {}
|
impl std::error::Error for UnmanagedResource {}
|
||||||
|
|
||||||
|
/// This resource has been destroyed and can no longer be used.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DeadResource;
|
||||||
|
|
||||||
|
impl std::fmt::Display for DeadResource {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str("This resource has been destroyed and can no longer be used.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for DeadResource {}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/// A rectangle defined by its top-left corner and dimensions
|
/// A rectangle defined by its top-left corner and dimensions
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||||
pub struct Rectangle {
|
pub struct Rectangle {
|
||||||
/// horizontal position of the top-left corner of the rectangle, in surface coordinates
|
/// horizontal position of the top-left corner of the rectangle, in surface coordinates
|
||||||
pub x: i32,
|
pub x: i32,
|
||||||
|
|
|
@ -186,17 +186,17 @@ macro_rules! define_roles(
|
||||||
($enum_name: ident) => {
|
($enum_name: ident) => {
|
||||||
define_roles!($enum_name =>);
|
define_roles!($enum_name =>);
|
||||||
};
|
};
|
||||||
($enum_name:ident => $([ $role_name: ident, $role_data: ty])*) => {
|
($enum_name:ident => $($(#[$role_attr:meta])* [$role_name: ident, $role_data: ty])*) => {
|
||||||
define_roles!(__impl $enum_name =>
|
define_roles!(__impl $enum_name =>
|
||||||
// add in subsurface role
|
// add in subsurface role
|
||||||
[Subsurface, $crate::wayland::compositor::SubsurfaceRole]
|
[Subsurface, $crate::wayland::compositor::SubsurfaceRole]
|
||||||
$([$role_name, $role_data])*
|
$($(#[$role_attr])* [$role_name, $role_data])*
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
(__impl $enum_name:ident => $([ $role_name: ident, $role_data: ty])*) => {
|
(__impl $enum_name:ident => $($(#[$role_attr:meta])* [$role_name: ident, $role_data: ty])*) => {
|
||||||
pub enum $enum_name {
|
pub enum $enum_name {
|
||||||
NoRole,
|
NoRole,
|
||||||
$($role_name($role_data)),*
|
$($(#[$role_attr])* $role_name($role_data)),*
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for $enum_name {
|
impl Default for $enum_name {
|
||||||
|
@ -216,6 +216,7 @@ macro_rules! define_roles(
|
||||||
}
|
}
|
||||||
|
|
||||||
$(
|
$(
|
||||||
|
$(#[$role_attr])*
|
||||||
impl $crate::wayland::compositor::roles::Role<$role_data> for $enum_name {
|
impl $crate::wayland::compositor::roles::Role<$role_data> for $enum_name {
|
||||||
fn set_with(&mut self, data: $role_data) -> ::std::result::Result<(), $role_data> {
|
fn set_with(&mut self, data: $role_data) -> ::std::result::Result<(), $role_data> {
|
||||||
if let $enum_name::NoRole = *self {
|
if let $enum_name::NoRole = *self {
|
||||||
|
|
|
@ -74,6 +74,8 @@ use wayland_server::{
|
||||||
Display, Filter, Global,
|
Display, Filter, Global,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::PingError;
|
||||||
|
|
||||||
mod wl_handlers;
|
mod wl_handlers;
|
||||||
|
|
||||||
/// Metadata associated with the `wl_surface` role
|
/// Metadata associated with the `wl_surface` role
|
||||||
|
@ -83,7 +85,7 @@ pub struct ShellSurfaceRole {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
/// Class of the surface
|
/// Class of the surface
|
||||||
pub class: String,
|
pub class: String,
|
||||||
pending_ping: Serial,
|
pending_ping: Option<Serial>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A handle to a shell surface
|
/// A handle to a shell surface
|
||||||
|
@ -139,24 +141,21 @@ where
|
||||||
/// down to 0 before a pong is received, mark the client as unresponsive.
|
/// down to 0 before a pong is received, mark the client as unresponsive.
|
||||||
///
|
///
|
||||||
/// Fails if this shell client already has a pending ping or is already dead.
|
/// Fails if this shell client already has a pending ping or is already dead.
|
||||||
pub fn send_ping(&self, serial: Serial) -> Result<(), ()> {
|
pub fn send_ping(&self, serial: Serial) -> Result<(), PingError> {
|
||||||
if !self.alive() {
|
if !self.alive() {
|
||||||
return Err(());
|
return Err(PingError::DeadSurface);
|
||||||
}
|
|
||||||
let ret = self.token.with_role_data(&self.wl_surface, |data| {
|
|
||||||
if data.pending_ping == Serial::from(0) {
|
|
||||||
data.pending_ping = serial;
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if let Ok(true) = ret {
|
|
||||||
self.shell_surface.ping(serial.into());
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(())
|
|
||||||
}
|
}
|
||||||
|
self.token
|
||||||
|
.with_role_data(&self.wl_surface, |data| {
|
||||||
|
if let Some(pending_ping) = data.pending_ping {
|
||||||
|
return Err(PingError::PingAlreadyPending(pending_ping));
|
||||||
|
}
|
||||||
|
data.pending_ping = Some(serial);
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.unwrap()?;
|
||||||
|
self.shell_surface.ping(serial.into());
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
|
|
|
@ -32,7 +32,7 @@ pub(crate) fn implement_shell<R, Impl>(
|
||||||
let role_data = ShellSurfaceRole {
|
let role_data = ShellSurfaceRole {
|
||||||
title: "".into(),
|
title: "".into(),
|
||||||
class: "".into(),
|
class: "".into(),
|
||||||
pending_ping: Serial::from(0),
|
pending_ping: None,
|
||||||
};
|
};
|
||||||
if ctoken.give_role_with(&surface, role_data).is_err() {
|
if ctoken.give_role_with(&surface, role_data).is_err() {
|
||||||
shell
|
shell
|
||||||
|
@ -102,8 +102,8 @@ where
|
||||||
let serial = Serial::from(serial);
|
let serial = Serial::from(serial);
|
||||||
let valid = ctoken
|
let valid = ctoken
|
||||||
.with_role_data(&data.surface, |data| {
|
.with_role_data(&data.surface, |data| {
|
||||||
if data.pending_ping == serial {
|
if data.pending_ping == Some(serial) {
|
||||||
data.pending_ping = Serial::from(0);
|
data.pending_ping = None;
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|
|
@ -14,5 +14,20 @@
|
||||||
//! - The [`legacy`](legacy/index.html) module provides handlers for the `wl_shell` protocol, which
|
//! - The [`legacy`](legacy/index.html) module provides handlers for the `wl_shell` protocol, which
|
||||||
//! is now deprecated. You only need it if you want to support apps predating `xdg_shell`.
|
//! is now deprecated. You only need it if you want to support apps predating `xdg_shell`.
|
||||||
|
|
||||||
|
use super::Serial;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
pub mod legacy;
|
pub mod legacy;
|
||||||
pub mod xdg;
|
pub mod xdg;
|
||||||
|
|
||||||
|
/// Represents the possible errors returned from
|
||||||
|
/// a surface ping
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum PingError {
|
||||||
|
/// The operation failed because the underlying surface has been destroyed
|
||||||
|
#[error("the ping failed cause the underlying surface has been destroyed")]
|
||||||
|
DeadSurface,
|
||||||
|
/// There is already a pending ping
|
||||||
|
#[error("there is already a ping pending `{0:?}`")]
|
||||||
|
PingAlreadyPending(Serial),
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,7 @@
|
||||||
use std::{cell::RefCell, ops::Deref as _, sync::Mutex};
|
use std::{cell::RefCell, ops::Deref as _, sync::Mutex};
|
||||||
|
|
||||||
use crate::wayland::compositor::{roles::*, CompositorToken};
|
use crate::wayland::compositor::{roles::*, CompositorToken};
|
||||||
|
use crate::wayland::shell::xdg::{ConfigureError, PopupState};
|
||||||
use crate::wayland::Serial;
|
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,
|
||||||
|
@ -10,9 +11,9 @@ use wayland_server::{protocol::wl_surface, Filter, Main};
|
||||||
use crate::utils::Rectangle;
|
use crate::utils::Rectangle;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
make_shell_client_data, PopupConfigure, PopupKind, PopupState, PositionerState, ShellClient,
|
make_shell_client_data, PopupConfigure, PopupKind, PositionerState, ShellClient, ShellClientData,
|
||||||
ShellClientData, ShellData, ToplevelConfigure, ToplevelKind, ToplevelState, XdgRequest,
|
ShellData, ToplevelClientPending, ToplevelConfigure, ToplevelKind, XdgPopupSurfaceRole,
|
||||||
XdgSurfacePendingState, XdgSurfaceRole,
|
XdgPopupSurfaceRoleAttributes, XdgRequest, XdgToplevelSurfaceRole, XdgToplevelSurfaceRoleAttributes,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn implement_wm_base<R>(
|
pub(crate) fn implement_wm_base<R>(
|
||||||
|
@ -20,7 +21,7 @@ pub(crate) fn implement_wm_base<R>(
|
||||||
shell_data: &ShellData<R>,
|
shell_data: &ShellData<R>,
|
||||||
) -> xdg_wm_base::XdgWmBase
|
) -> xdg_wm_base::XdgWmBase
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
shell.quick_assign(|shell, req, _data| wm_implementation::<R>(req, shell.deref().clone()));
|
shell.quick_assign(|shell, req, _data| wm_implementation::<R>(req, shell.deref().clone()));
|
||||||
shell.as_ref().user_data().set(|| ShellUserData {
|
shell.as_ref().user_data().set(|| ShellUserData {
|
||||||
|
@ -55,7 +56,7 @@ pub(crate) fn make_shell_client<R>(
|
||||||
|
|
||||||
fn wm_implementation<R>(request: xdg_wm_base::Request, shell: xdg_wm_base::XdgWmBase)
|
fn wm_implementation<R>(request: xdg_wm_base::Request, shell: xdg_wm_base::XdgWmBase)
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = shell.as_ref().user_data().get::<ShellUserData<R>>().unwrap();
|
let data = shell.as_ref().user_data().get::<ShellUserData<R>>().unwrap();
|
||||||
match request {
|
match request {
|
||||||
|
@ -66,24 +67,9 @@ where
|
||||||
implement_positioner(id);
|
implement_positioner(id);
|
||||||
}
|
}
|
||||||
xdg_wm_base::Request::GetXdgSurface { id, surface } => {
|
xdg_wm_base::Request::GetXdgSurface { id, surface } => {
|
||||||
let role_data = XdgSurfaceRole {
|
// Do not assign a role to the surface here
|
||||||
pending_state: XdgSurfacePendingState::None,
|
// xdg_surface is not role, only xdg_toplevel and
|
||||||
window_geometry: None,
|
// xdg_popup are defined as roles
|
||||||
pending_configures: Vec::new(),
|
|
||||||
configured: false,
|
|
||||||
};
|
|
||||||
if data
|
|
||||||
.shell_data
|
|
||||||
.compositor_token
|
|
||||||
.give_role_with(&surface, role_data)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
shell.as_ref().post_error(
|
|
||||||
xdg_wm_base::Error::Role as u32,
|
|
||||||
"Surface already has a role.".into(),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
id.quick_assign(|surface, req, _data| {
|
id.quick_assign(|surface, req, _data| {
|
||||||
xdg_surface_implementation::<R>(req, surface.deref().clone())
|
xdg_surface_implementation::<R>(req, surface.deref().clone())
|
||||||
});
|
});
|
||||||
|
@ -98,8 +84,8 @@ where
|
||||||
let serial = Serial::from(serial);
|
let serial = Serial::from(serial);
|
||||||
let valid = {
|
let valid = {
|
||||||
let mut guard = data.client_data.lock().unwrap();
|
let mut guard = data.client_data.lock().unwrap();
|
||||||
if guard.pending_ping == serial {
|
if guard.pending_ping == Some(serial) {
|
||||||
guard.pending_ping = Serial::from(0);
|
guard.pending_ping = None;
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -191,7 +177,7 @@ struct XdgSurfaceUserData<R> {
|
||||||
|
|
||||||
fn destroy_surface<R>(surface: xdg_surface::XdgSurface)
|
fn destroy_surface<R>(surface: xdg_surface::XdgSurface)
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = surface
|
let data = surface
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -204,24 +190,36 @@ where
|
||||||
// disconnecting client), ignore the protocol check.
|
// disconnecting client), ignore the protocol check.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
data.shell_data
|
|
||||||
|
if !data.shell_data.compositor_token.has_a_role(&data.wl_surface) {
|
||||||
|
// No role assigned to the surface, we can exit early.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let has_active_xdg_role = data
|
||||||
|
.shell_data
|
||||||
.compositor_token
|
.compositor_token
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |rdata| {
|
.with_role_data(&data.wl_surface, |role: &mut XdgToplevelSurfaceRole| {
|
||||||
if let XdgSurfacePendingState::None = rdata.pending_state {
|
role.is_some()
|
||||||
// all is good
|
|
||||||
} else {
|
|
||||||
data.wm_base.as_ref().post_error(
|
|
||||||
xdg_wm_base::Error::Role as u32,
|
|
||||||
"xdg_surface was destroyed before its role object".into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
.unwrap_or(false)
|
||||||
|
|| data
|
||||||
|
.shell_data
|
||||||
|
.compositor_token
|
||||||
|
.with_role_data(&data.wl_surface, |role: &mut XdgPopupSurfaceRole| role.is_some())
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if has_active_xdg_role {
|
||||||
|
data.wm_base.as_ref().post_error(
|
||||||
|
xdg_wm_base::Error::Role as u32,
|
||||||
|
"xdg_surface was destroyed before its role object".into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xdg_surface_implementation<R>(request: xdg_surface::Request, xdg_surface: xdg_surface::XdgSurface)
|
fn xdg_surface_implementation<R>(request: xdg_surface::Request, xdg_surface: xdg_surface::XdgSurface)
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = xdg_surface
|
let data = xdg_surface
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -233,18 +231,25 @@ where
|
||||||
// all is handled by our destructor
|
// all is handled by our destructor
|
||||||
}
|
}
|
||||||
xdg_surface::Request::GetToplevel { id } => {
|
xdg_surface::Request::GetToplevel { id } => {
|
||||||
data.shell_data
|
// We now can assign a role to the surface
|
||||||
|
let surface = &data.wl_surface;
|
||||||
|
let shell = &data.wm_base;
|
||||||
|
|
||||||
|
let role_data = XdgToplevelSurfaceRole::Some(Default::default());
|
||||||
|
|
||||||
|
if data
|
||||||
|
.shell_data
|
||||||
.compositor_token
|
.compositor_token
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
.give_role_with(&surface, role_data)
|
||||||
data.pending_state = XdgSurfacePendingState::Toplevel(ToplevelState {
|
.is_err()
|
||||||
parent: None,
|
{
|
||||||
title: String::new(),
|
shell.as_ref().post_error(
|
||||||
app_id: String::new(),
|
xdg_wm_base::Error::Role as u32,
|
||||||
min_size: (0, 0),
|
"Surface already has a role.".into(),
|
||||||
max_size: (0, 0),
|
);
|
||||||
});
|
return;
|
||||||
})
|
}
|
||||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
|
||||||
id.quick_assign(|toplevel, req, _data| {
|
id.quick_assign(|toplevel, req, _data| {
|
||||||
toplevel_implementation::<R>(req, toplevel.deref().clone())
|
toplevel_implementation::<R>(req, toplevel.deref().clone())
|
||||||
});
|
});
|
||||||
|
@ -276,7 +281,9 @@ where
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<RefCell<PositionerState>>()
|
.get::<RefCell<PositionerState>>()
|
||||||
.unwrap();
|
.unwrap()
|
||||||
|
.borrow()
|
||||||
|
.clone();
|
||||||
|
|
||||||
let parent_surface = parent.map(|parent| {
|
let parent_surface = parent.map(|parent| {
|
||||||
let parent_data = parent
|
let parent_data = parent
|
||||||
|
@ -286,16 +293,34 @@ where
|
||||||
.unwrap();
|
.unwrap();
|
||||||
parent_data.wl_surface.clone()
|
parent_data.wl_surface.clone()
|
||||||
});
|
});
|
||||||
data.shell_data
|
|
||||||
|
// We now can assign a role to the surface
|
||||||
|
let surface = &data.wl_surface;
|
||||||
|
let shell = &data.wm_base;
|
||||||
|
|
||||||
|
let role_data = XdgPopupSurfaceRole::Some(XdgPopupSurfaceRoleAttributes {
|
||||||
|
parent: parent_surface,
|
||||||
|
server_pending: Some(PopupState {
|
||||||
|
// Set the positioner data as the popup geometry
|
||||||
|
geometry: positioner_data.get_geometry(),
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
if data
|
||||||
|
.shell_data
|
||||||
.compositor_token
|
.compositor_token
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
.give_role_with(&surface, role_data)
|
||||||
data.pending_state = XdgSurfacePendingState::Popup(PopupState {
|
.is_err()
|
||||||
parent: parent_surface,
|
{
|
||||||
positioner: positioner_data.borrow().clone(),
|
shell.as_ref().post_error(
|
||||||
});
|
xdg_wm_base::Error::Role as u32,
|
||||||
})
|
"Surface already has a role.".into(),
|
||||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
);
|
||||||
id.quick_assign(|popup, req, _data| xg_popup_implementation::<R>(req, popup.deref().clone()));
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
id.quick_assign(|popup, req, _data| xdg_popup_implementation::<R>(req, popup.deref().clone()));
|
||||||
id.assign_destructor(Filter::new(|popup, _, _data| destroy_popup::<R>(popup)));
|
id.assign_destructor(Filter::new(|popup, _, _data| destroy_popup::<R>(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(),
|
||||||
|
@ -316,40 +341,93 @@ where
|
||||||
(&mut *user_impl)(XdgRequest::NewPopup { surface: handle });
|
(&mut *user_impl)(XdgRequest::NewPopup { surface: handle });
|
||||||
}
|
}
|
||||||
xdg_surface::Request::SetWindowGeometry { x, y, width, height } => {
|
xdg_surface::Request::SetWindowGeometry { x, y, width, height } => {
|
||||||
data.shell_data
|
// Check the role of the surface, this can be either xdg_toplevel
|
||||||
|
// or xdg_popup. If none of the role matches the xdg_surface has no role set
|
||||||
|
// which is a protocol error.
|
||||||
|
let surface = &data.wl_surface;
|
||||||
|
|
||||||
|
if !data.shell_data.compositor_token.has_a_role(surface) {
|
||||||
|
data.wm_base.as_ref().post_error(
|
||||||
|
xdg_surface::Error::NotConstructed as u32,
|
||||||
|
"xdg_surface must have a role.".into(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the next window geometry here, the geometry will be moved from
|
||||||
|
// next to the current geometry on a commit. This has to be done currently
|
||||||
|
// in anvil as the whole commit logic is implemented there until a proper
|
||||||
|
// abstraction has been found to handle commits within roles. This also
|
||||||
|
// ensures that a commit for a xdg_surface follows the rules for subsurfaces.
|
||||||
|
let has_wrong_role = data
|
||||||
|
.shell_data
|
||||||
.compositor_token
|
.compositor_token
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
.with_xdg_role(surface, |role| {
|
||||||
data.window_geometry = Some(Rectangle { x, y, width, height });
|
role.set_window_geometry(Rectangle { x, y, width, height })
|
||||||
})
|
})
|
||||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
.is_err();
|
||||||
|
|
||||||
|
if has_wrong_role {
|
||||||
|
data.wm_base.as_ref().post_error(
|
||||||
|
xdg_wm_base::Error::Role as u32,
|
||||||
|
"xdg_surface must have a role of xdg_toplevel or xdg_popup.".into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
xdg_surface::Request::AckConfigure { serial } => {
|
xdg_surface::Request::AckConfigure { serial } => {
|
||||||
data.shell_data
|
let serial = Serial::from(serial);
|
||||||
|
let surface = &data.wl_surface;
|
||||||
|
|
||||||
|
// Check the role of the surface, this can be either xdg_toplevel
|
||||||
|
// or xdg_popup. If none of the role matches the xdg_surface has no role set
|
||||||
|
// which is a protocol error.
|
||||||
|
if !data.shell_data.compositor_token.has_a_role(surface) {
|
||||||
|
data.wm_base.as_ref().post_error(
|
||||||
|
xdg_surface::Error::NotConstructed as u32,
|
||||||
|
"xdg_surface must have a role.".into(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the correct configure state for the provided serial
|
||||||
|
// discard all configure states that are older than the provided
|
||||||
|
// serial.
|
||||||
|
// If no matching serial can be found raise a protocol error
|
||||||
|
//
|
||||||
|
// Invoke the user impl with the found configuration
|
||||||
|
// This has to include the serial and the role specific data.
|
||||||
|
// - For xdg_popup there is no data.
|
||||||
|
// - For xdg_toplevel send the state data including
|
||||||
|
// width, height, min/max size, maximized, fullscreen, resizing, activated
|
||||||
|
//
|
||||||
|
// This can be used to integrate custom protocol extensions
|
||||||
|
//
|
||||||
|
let configure = match data
|
||||||
|
.shell_data
|
||||||
.compositor_token
|
.compositor_token
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |role_data| {
|
.with_xdg_role(surface, |role| role.ack_configure(serial))
|
||||||
let mut found = false;
|
{
|
||||||
role_data.pending_configures.retain(|&s| {
|
Ok(Ok(configure)) => configure,
|
||||||
if s == serial {
|
Ok(Err(ConfigureError::SerialNotFound(serial))) => {
|
||||||
found = true;
|
data.wm_base.as_ref().post_error(
|
||||||
}
|
xdg_wm_base::Error::InvalidSurfaceState as u32,
|
||||||
s > serial
|
format!("wrong configure serial: {}", <u32>::from(serial)),
|
||||||
});
|
);
|
||||||
if !found {
|
return;
|
||||||
// client responded to a non-existing configure
|
}
|
||||||
data.wm_base.as_ref().post_error(
|
Err(_) => {
|
||||||
xdg_wm_base::Error::InvalidSurfaceState as u32,
|
data.wm_base.as_ref().post_error(
|
||||||
format!("Wrong configure serial: {}", serial),
|
xdg_wm_base::Error::Role as u32,
|
||||||
);
|
"xdg_surface must have a role of xdg_toplevel or xdg_popup.".into(),
|
||||||
}
|
);
|
||||||
role_data.configured = true;
|
return;
|
||||||
})
|
}
|
||||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
};
|
||||||
|
|
||||||
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);
|
|
||||||
(&mut *user_impl)(XdgRequest::AckConfigure {
|
(&mut *user_impl)(XdgRequest::AckConfigure {
|
||||||
surface: data.wl_surface.clone(),
|
surface: surface.clone(),
|
||||||
serial,
|
configure,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
@ -368,38 +446,62 @@ pub(crate) struct ShellSurfaceUserData<R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility functions allowing to factor out a lot of the upcoming logic
|
// Utility functions allowing to factor out a lot of the upcoming logic
|
||||||
fn with_surface_toplevel_data<R, F>(shell_data: &ShellData<R>, toplevel: &xdg_toplevel::XdgToplevel, f: F)
|
fn with_surface_toplevel_role_data<R, F, T>(
|
||||||
|
shell_data: &ShellData<R>,
|
||||||
|
toplevel: &xdg_toplevel::XdgToplevel,
|
||||||
|
f: F,
|
||||||
|
) -> T
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||||
F: FnOnce(&mut ToplevelState),
|
F: FnOnce(&mut XdgToplevelSurfaceRoleAttributes) -> T,
|
||||||
{
|
{
|
||||||
let toplevel_data = toplevel
|
let data = toplevel
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData<R>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
shell_data
|
shell_data
|
||||||
.compositor_token
|
.compositor_token
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&toplevel_data.wl_surface, |data| match data.pending_state {
|
.with_role_data::<XdgToplevelSurfaceRole, _, _>(&data.wl_surface, |role| {
|
||||||
XdgSurfacePendingState::Toplevel(ref mut toplevel_data) => f(toplevel_data),
|
let attributes = role
|
||||||
_ => unreachable!(),
|
.as_mut()
|
||||||
|
.expect("xdg_toplevel exists but role has been destroyed?!");
|
||||||
|
f(attributes)
|
||||||
})
|
})
|
||||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
.expect("xdg_toplevel exists but surface has not shell_surface role?!")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_surface_toplevel_client_pending<R, F, T>(
|
||||||
|
shell_data: &ShellData<R>,
|
||||||
|
toplevel: &xdg_toplevel::XdgToplevel,
|
||||||
|
f: F,
|
||||||
|
) -> T
|
||||||
|
where
|
||||||
|
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||||
|
F: FnOnce(&mut ToplevelClientPending) -> T,
|
||||||
|
{
|
||||||
|
with_surface_toplevel_role_data(shell_data, toplevel, |data| {
|
||||||
|
if data.client_pending.is_none() {
|
||||||
|
data.client_pending = Some(Default::default());
|
||||||
|
}
|
||||||
|
f(&mut data.client_pending.as_mut().unwrap())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_toplevel_configure<R>(resource: &xdg_toplevel::XdgToplevel, configure: ToplevelConfigure)
|
pub fn send_toplevel_configure<R>(resource: &xdg_toplevel::XdgToplevel, configure: ToplevelConfigure)
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = resource
|
let data = resource
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData<R>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (width, height) = configure.size.unwrap_or((0, 0));
|
|
||||||
|
let (width, height) = configure.state.size.unwrap_or((0, 0));
|
||||||
// convert the Vec<State> (which is really a Vec<u32>) into Vec<u8>
|
// convert the Vec<State> (which is really a Vec<u32>) into Vec<u8>
|
||||||
let states = {
|
let states = {
|
||||||
let mut states = configure.states;
|
let mut states: Vec<xdg_toplevel::State> = configure.state.states.into();
|
||||||
let ptr = states.as_mut_ptr();
|
let ptr = states.as_mut_ptr();
|
||||||
let len = states.len();
|
let len = states.len();
|
||||||
let cap = states.capacity();
|
let cap = states.capacity();
|
||||||
|
@ -407,15 +509,13 @@ where
|
||||||
unsafe { Vec::from_raw_parts(ptr as *mut u8, len * 4, cap * 4) }
|
unsafe { Vec::from_raw_parts(ptr as *mut u8, len * 4, cap * 4) }
|
||||||
};
|
};
|
||||||
let serial = configure.serial;
|
let serial = configure.serial;
|
||||||
|
|
||||||
|
// Send the toplevel configure
|
||||||
resource.configure(width, height, states);
|
resource.configure(width, height, states);
|
||||||
|
|
||||||
|
// Send the base xdg_surface configure event to mark
|
||||||
|
// The configure as finished
|
||||||
data.xdg_surface.configure(serial.into());
|
data.xdg_surface.configure(serial.into());
|
||||||
// Add the configure as pending
|
|
||||||
data.shell_data
|
|
||||||
.compositor_token
|
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
|
||||||
data.pending_configures.push(serial.into())
|
|
||||||
})
|
|
||||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_toplevel_handle<R: 'static>(resource: &xdg_toplevel::XdgToplevel) -> super::ToplevelSurface<R> {
|
fn make_toplevel_handle<R: 'static>(resource: &xdg_toplevel::XdgToplevel) -> super::ToplevelSurface<R> {
|
||||||
|
@ -433,7 +533,7 @@ fn make_toplevel_handle<R: 'static>(resource: &xdg_toplevel::XdgToplevel) -> sup
|
||||||
|
|
||||||
fn toplevel_implementation<R>(request: xdg_toplevel::Request, toplevel: xdg_toplevel::XdgToplevel)
|
fn toplevel_implementation<R>(request: xdg_toplevel::Request, toplevel: xdg_toplevel::XdgToplevel)
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = toplevel
|
let data = toplevel
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -445,8 +545,9 @@ where
|
||||||
// all it done by the destructor
|
// all it done by the destructor
|
||||||
}
|
}
|
||||||
xdg_toplevel::Request::SetParent { parent } => {
|
xdg_toplevel::Request::SetParent { parent } => {
|
||||||
with_surface_toplevel_data(&data.shell_data, &toplevel, |toplevel_data| {
|
// Parent is not double buffered, we can set it directly
|
||||||
toplevel_data.parent = parent.map(|toplevel_surface_parent| {
|
with_surface_toplevel_role_data(&data.shell_data, &toplevel, |data| {
|
||||||
|
data.parent = parent.map(|toplevel_surface_parent| {
|
||||||
toplevel_surface_parent
|
toplevel_surface_parent
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
|
@ -458,16 +559,19 @@ where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
xdg_toplevel::Request::SetTitle { title } => {
|
xdg_toplevel::Request::SetTitle { title } => {
|
||||||
with_surface_toplevel_data(&data.shell_data, &toplevel, |toplevel_data| {
|
// Title is not double buffered, we can set it directly
|
||||||
toplevel_data.title = title;
|
with_surface_toplevel_role_data(&data.shell_data, &toplevel, |data| {
|
||||||
|
data.title = Some(title);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
xdg_toplevel::Request::SetAppId { app_id } => {
|
xdg_toplevel::Request::SetAppId { app_id } => {
|
||||||
with_surface_toplevel_data(&data.shell_data, &toplevel, |toplevel_data| {
|
// AppId is not double buffered, we can set it directly
|
||||||
toplevel_data.app_id = app_id;
|
with_surface_toplevel_role_data(&data.shell_data, &toplevel, |role| {
|
||||||
|
role.app_id = Some(app_id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
xdg_toplevel::Request::ShowWindowMenu { seat, serial, x, y } => {
|
xdg_toplevel::Request::ShowWindowMenu { seat, serial, x, y } => {
|
||||||
|
// 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();
|
||||||
|
@ -479,6 +583,7 @@ where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
xdg_toplevel::Request::Move { seat, serial } => {
|
xdg_toplevel::Request::Move { seat, serial } => {
|
||||||
|
// 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();
|
||||||
|
@ -489,6 +594,7 @@ where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
xdg_toplevel::Request::Resize { seat, serial, edges } => {
|
xdg_toplevel::Request::Resize { seat, serial, edges } => {
|
||||||
|
// 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);
|
||||||
|
@ -500,13 +606,13 @@ where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
xdg_toplevel::Request::SetMaxSize { width, height } => {
|
xdg_toplevel::Request::SetMaxSize { width, height } => {
|
||||||
with_surface_toplevel_data(&data.shell_data, &toplevel, |toplevel_data| {
|
with_surface_toplevel_client_pending(&data.shell_data, &toplevel, |toplevel_data| {
|
||||||
toplevel_data.max_size = (width, height);
|
toplevel_data.max_size = Some((width, height));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
xdg_toplevel::Request::SetMinSize { width, height } => {
|
xdg_toplevel::Request::SetMinSize { width, height } => {
|
||||||
with_surface_toplevel_data(&data.shell_data, &toplevel, |toplevel_data| {
|
with_surface_toplevel_client_pending(&data.shell_data, &toplevel, |toplevel_data| {
|
||||||
toplevel_data.min_size = (width, height);
|
toplevel_data.min_size = Some((width, height));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
xdg_toplevel::Request::SetMaximized => {
|
xdg_toplevel::Request::SetMaximized => {
|
||||||
|
@ -533,6 +639,8 @@ where
|
||||||
(&mut *user_impl)(XdgRequest::UnFullscreen { surface: handle });
|
(&mut *user_impl)(XdgRequest::UnFullscreen { surface: handle });
|
||||||
}
|
}
|
||||||
xdg_toplevel::Request::SetMinimized => {
|
xdg_toplevel::Request::SetMinimized => {
|
||||||
|
// This has to be handled by the compositor, may not be
|
||||||
|
// 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 });
|
||||||
|
@ -543,7 +651,7 @@ where
|
||||||
|
|
||||||
fn destroy_toplevel<R>(toplevel: xdg_toplevel::XdgToplevel)
|
fn destroy_toplevel<R>(toplevel: xdg_toplevel::XdgToplevel)
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = toplevel
|
let data = toplevel
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -557,9 +665,8 @@ where
|
||||||
} else {
|
} else {
|
||||||
data.shell_data
|
data.shell_data
|
||||||
.compositor_token
|
.compositor_token
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
.with_role_data(&data.wl_surface, |role_data| {
|
||||||
data.pending_state = XdgSurfacePendingState::None;
|
*role_data = XdgToplevelSurfaceRole::None;
|
||||||
data.configured = false;
|
|
||||||
})
|
})
|
||||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
||||||
}
|
}
|
||||||
|
@ -578,25 +685,23 @@ where
|
||||||
|
|
||||||
pub(crate) fn send_popup_configure<R>(resource: &xdg_popup::XdgPopup, configure: PopupConfigure)
|
pub(crate) fn send_popup_configure<R>(resource: &xdg_popup::XdgPopup, configure: PopupConfigure)
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = resource
|
let data = resource
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData<R>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (x, y) = configure.position;
|
|
||||||
let (width, height) = configure.size;
|
|
||||||
let serial = configure.serial;
|
let serial = configure.serial;
|
||||||
resource.configure(x, y, width, height);
|
let geometry = configure.state.geometry;
|
||||||
|
|
||||||
|
// Send the popup configure
|
||||||
|
resource.configure(geometry.x, geometry.y, geometry.width, geometry.height);
|
||||||
|
|
||||||
|
// Send the base xdg_surface configure event to mark
|
||||||
|
// the configure as finished
|
||||||
data.xdg_surface.configure(serial.into());
|
data.xdg_surface.configure(serial.into());
|
||||||
// Add the configure as pending
|
|
||||||
data.shell_data
|
|
||||||
.compositor_token
|
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
|
||||||
data.pending_configures.push(serial.into())
|
|
||||||
})
|
|
||||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_popup_handle<R: 'static>(resource: &xdg_popup::XdgPopup) -> super::PopupSurface<R> {
|
fn make_popup_handle<R: 'static>(resource: &xdg_popup::XdgPopup) -> super::PopupSurface<R> {
|
||||||
|
@ -612,9 +717,9 @@ fn make_popup_handle<R: 'static>(resource: &xdg_popup::XdgPopup) -> super::Popup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xg_popup_implementation<R>(request: xdg_popup::Request, popup: xdg_popup::XdgPopup)
|
fn xdg_popup_implementation<R>(request: xdg_popup::Request, popup: xdg_popup::XdgPopup)
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = popup
|
let data = popup
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -641,7 +746,7 @@ where
|
||||||
|
|
||||||
fn destroy_popup<R>(popup: xdg_popup::XdgPopup)
|
fn destroy_popup<R>(popup: xdg_popup::XdgPopup)
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = popup
|
let data = popup
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -655,9 +760,8 @@ where
|
||||||
} else {
|
} else {
|
||||||
data.shell_data
|
data.shell_data
|
||||||
.compositor_token
|
.compositor_token
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
.with_role_data(&data.wl_surface, |role_data| {
|
||||||
data.pending_state = XdgSurfacePendingState::None;
|
*role_data = XdgPopupSurfaceRole::None;
|
||||||
data.configured = false;
|
|
||||||
})
|
})
|
||||||
.expect("xdg_popup exists but surface has not shell_surface role?!");
|
.expect("xdg_popup exists but surface has not shell_surface role?!");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::{cell::RefCell, ops::Deref as _, sync::Mutex};
|
use std::{cell::RefCell, ops::Deref as _, sync::Mutex};
|
||||||
|
|
||||||
use crate::wayland::compositor::{roles::*, CompositorToken};
|
use crate::wayland::compositor::{roles::*, CompositorToken};
|
||||||
|
use crate::wayland::shell::xdg::{ConfigureError, PopupState};
|
||||||
use crate::wayland::Serial;
|
use crate::wayland::Serial;
|
||||||
use wayland_protocols::{
|
use wayland_protocols::{
|
||||||
unstable::xdg_shell::v6::server::{
|
unstable::xdg_shell::v6::server::{
|
||||||
|
@ -13,9 +14,9 @@ use wayland_server::{protocol::wl_surface, Filter, Main};
|
||||||
use crate::utils::Rectangle;
|
use crate::utils::Rectangle;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
make_shell_client_data, PopupConfigure, PopupKind, PopupState, PositionerState, ShellClient,
|
make_shell_client_data, PopupConfigure, PopupKind, PositionerState, ShellClient, ShellClientData,
|
||||||
ShellClientData, ShellData, ToplevelConfigure, ToplevelKind, ToplevelState, XdgRequest,
|
ShellData, ToplevelClientPending, ToplevelConfigure, ToplevelKind, XdgPopupSurfaceRole,
|
||||||
XdgSurfacePendingState, XdgSurfaceRole,
|
XdgPopupSurfaceRoleAttributes, XdgRequest, XdgToplevelSurfaceRole, XdgToplevelSurfaceRoleAttributes,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn implement_shell<R>(
|
pub(crate) fn implement_shell<R>(
|
||||||
|
@ -23,7 +24,7 @@ pub(crate) fn implement_shell<R>(
|
||||||
shell_data: &ShellData<R>,
|
shell_data: &ShellData<R>,
|
||||||
) -> zxdg_shell_v6::ZxdgShellV6
|
) -> zxdg_shell_v6::ZxdgShellV6
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
shell.quick_assign(|shell, req, _data| shell_implementation::<R>(req, shell.deref().clone()));
|
shell.quick_assign(|shell, req, _data| shell_implementation::<R>(req, shell.deref().clone()));
|
||||||
shell.as_ref().user_data().set(|| ShellUserData {
|
shell.as_ref().user_data().set(|| ShellUserData {
|
||||||
|
@ -58,7 +59,7 @@ pub(crate) fn make_shell_client<R>(
|
||||||
|
|
||||||
fn shell_implementation<R>(request: zxdg_shell_v6::Request, shell: zxdg_shell_v6::ZxdgShellV6)
|
fn shell_implementation<R>(request: zxdg_shell_v6::Request, shell: zxdg_shell_v6::ZxdgShellV6)
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = shell.as_ref().user_data().get::<ShellUserData<R>>().unwrap();
|
let data = shell.as_ref().user_data().get::<ShellUserData<R>>().unwrap();
|
||||||
match request {
|
match request {
|
||||||
|
@ -69,24 +70,6 @@ where
|
||||||
implement_positioner(id);
|
implement_positioner(id);
|
||||||
}
|
}
|
||||||
zxdg_shell_v6::Request::GetXdgSurface { id, surface } => {
|
zxdg_shell_v6::Request::GetXdgSurface { id, surface } => {
|
||||||
let role_data = XdgSurfaceRole {
|
|
||||||
pending_state: XdgSurfacePendingState::None,
|
|
||||||
window_geometry: None,
|
|
||||||
pending_configures: Vec::new(),
|
|
||||||
configured: false,
|
|
||||||
};
|
|
||||||
if data
|
|
||||||
.shell_data
|
|
||||||
.compositor_token
|
|
||||||
.give_role_with(&surface, role_data)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
shell.as_ref().post_error(
|
|
||||||
zxdg_shell_v6::Error::Role as u32,
|
|
||||||
"Surface already has a role.".into(),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
id.quick_assign(|surface, req, _data| {
|
id.quick_assign(|surface, req, _data| {
|
||||||
xdg_surface_implementation::<R>(req, surface.deref().clone())
|
xdg_surface_implementation::<R>(req, surface.deref().clone())
|
||||||
});
|
});
|
||||||
|
@ -98,10 +81,11 @@ where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
zxdg_shell_v6::Request::Pong { serial } => {
|
zxdg_shell_v6::Request::Pong { serial } => {
|
||||||
|
let serial = Serial::from(serial);
|
||||||
let valid = {
|
let valid = {
|
||||||
let mut guard = data.client_data.lock().unwrap();
|
let mut guard = data.client_data.lock().unwrap();
|
||||||
if guard.pending_ping == Serial::from(serial) {
|
if guard.pending_ping == Some(serial) {
|
||||||
guard.pending_ping = Serial::from(0);
|
guard.pending_ping = None;
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -209,7 +193,7 @@ struct XdgSurfaceUserData<R> {
|
||||||
|
|
||||||
fn destroy_surface<R>(surface: zxdg_surface_v6::ZxdgSurfaceV6)
|
fn destroy_surface<R>(surface: zxdg_surface_v6::ZxdgSurfaceV6)
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = surface
|
let data = surface
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -222,26 +206,38 @@ where
|
||||||
// disconnecting client), ignore the protocol check.
|
// disconnecting client), ignore the protocol check.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
data.shell_data
|
|
||||||
|
if !data.shell_data.compositor_token.has_a_role(&data.wl_surface) {
|
||||||
|
// No role assigned to the surface, we can exit early.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let has_active_xdg_role = data
|
||||||
|
.shell_data
|
||||||
.compositor_token
|
.compositor_token
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |rdata| {
|
.with_role_data(&data.wl_surface, |role: &mut XdgToplevelSurfaceRole| {
|
||||||
if let XdgSurfacePendingState::None = rdata.pending_state {
|
role.is_some()
|
||||||
// all is good
|
|
||||||
} else {
|
|
||||||
data.shell.as_ref().post_error(
|
|
||||||
zxdg_shell_v6::Error::Role as u32,
|
|
||||||
"xdg_surface was destroyed before its role object".into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
.unwrap_or(false)
|
||||||
|
|| data
|
||||||
|
.shell_data
|
||||||
|
.compositor_token
|
||||||
|
.with_role_data(&data.wl_surface, |role: &mut XdgPopupSurfaceRole| role.is_some())
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if has_active_xdg_role {
|
||||||
|
data.shell.as_ref().post_error(
|
||||||
|
zxdg_shell_v6::Error::Role as u32,
|
||||||
|
"xdg_surface was destroyed before its role object".into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xdg_surface_implementation<R>(
|
fn xdg_surface_implementation<R>(
|
||||||
request: zxdg_surface_v6::Request,
|
request: zxdg_surface_v6::Request,
|
||||||
xdg_surface: zxdg_surface_v6::ZxdgSurfaceV6,
|
xdg_surface: zxdg_surface_v6::ZxdgSurfaceV6,
|
||||||
) where
|
) where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = xdg_surface
|
let data = xdg_surface
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -253,18 +249,25 @@ fn xdg_surface_implementation<R>(
|
||||||
// all is handled by our destructor
|
// all is handled by our destructor
|
||||||
}
|
}
|
||||||
zxdg_surface_v6::Request::GetToplevel { id } => {
|
zxdg_surface_v6::Request::GetToplevel { id } => {
|
||||||
data.shell_data
|
// We now can assign a role to the surface
|
||||||
|
let surface = &data.wl_surface;
|
||||||
|
let shell = &data.shell;
|
||||||
|
|
||||||
|
let role_data = XdgToplevelSurfaceRole::Some(Default::default());
|
||||||
|
|
||||||
|
if data
|
||||||
|
.shell_data
|
||||||
.compositor_token
|
.compositor_token
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
.give_role_with(&surface, role_data)
|
||||||
data.pending_state = XdgSurfacePendingState::Toplevel(ToplevelState {
|
.is_err()
|
||||||
parent: None,
|
{
|
||||||
title: String::new(),
|
shell.as_ref().post_error(
|
||||||
app_id: String::new(),
|
zxdg_shell_v6::Error::Role as u32,
|
||||||
min_size: (0, 0),
|
"Surface already has a role.".into(),
|
||||||
max_size: (0, 0),
|
);
|
||||||
});
|
return;
|
||||||
})
|
}
|
||||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
|
||||||
id.quick_assign(|toplevel, req, _data| {
|
id.quick_assign(|toplevel, req, _data| {
|
||||||
toplevel_implementation::<R>(req, toplevel.deref().clone())
|
toplevel_implementation::<R>(req, toplevel.deref().clone())
|
||||||
});
|
});
|
||||||
|
@ -296,22 +299,45 @@ fn xdg_surface_implementation<R>(
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<RefCell<PositionerState>>()
|
.get::<RefCell<PositionerState>>()
|
||||||
.unwrap();
|
.unwrap()
|
||||||
|
.borrow()
|
||||||
|
.clone();
|
||||||
|
|
||||||
let parent_data = parent
|
let parent_surface = {
|
||||||
.as_ref()
|
let parent_data = parent
|
||||||
.user_data()
|
.as_ref()
|
||||||
.get::<XdgSurfaceUserData<R>>()
|
.user_data()
|
||||||
.unwrap();
|
.get::<XdgSurfaceUserData<R>>()
|
||||||
data.shell_data
|
.unwrap();
|
||||||
|
parent_data.wl_surface.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
// We now can assign a role to the surface
|
||||||
|
let surface = &data.wl_surface;
|
||||||
|
let shell = &data.shell;
|
||||||
|
|
||||||
|
let role_data = XdgPopupSurfaceRole::Some(XdgPopupSurfaceRoleAttributes {
|
||||||
|
parent: Some(parent_surface),
|
||||||
|
server_pending: Some(PopupState {
|
||||||
|
// Set the positioner data as the popup geometry
|
||||||
|
geometry: positioner_data.get_geometry(),
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
if data
|
||||||
|
.shell_data
|
||||||
.compositor_token
|
.compositor_token
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
.give_role_with(&surface, role_data)
|
||||||
data.pending_state = XdgSurfacePendingState::Popup(PopupState {
|
.is_err()
|
||||||
parent: Some(parent_data.wl_surface.clone()),
|
{
|
||||||
positioner: positioner_data.borrow().clone(),
|
shell.as_ref().post_error(
|
||||||
});
|
zxdg_shell_v6::Error::Role as u32,
|
||||||
})
|
"Surface already has a role.".into(),
|
||||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
id.quick_assign(|popup, req, _data| popup_implementation::<R>(req, popup.deref().clone()));
|
id.quick_assign(|popup, req, _data| popup_implementation::<R>(req, popup.deref().clone()));
|
||||||
id.assign_destructor(Filter::new(|popup, _, _data| destroy_popup::<R>(popup)));
|
id.assign_destructor(Filter::new(|popup, _, _data| destroy_popup::<R>(popup)));
|
||||||
id.as_ref().user_data().set(|| ShellSurfaceUserData {
|
id.as_ref().user_data().set(|| ShellSurfaceUserData {
|
||||||
|
@ -333,40 +359,93 @@ fn xdg_surface_implementation<R>(
|
||||||
(&mut *user_impl)(XdgRequest::NewPopup { surface: handle });
|
(&mut *user_impl)(XdgRequest::NewPopup { surface: handle });
|
||||||
}
|
}
|
||||||
zxdg_surface_v6::Request::SetWindowGeometry { x, y, width, height } => {
|
zxdg_surface_v6::Request::SetWindowGeometry { x, y, width, height } => {
|
||||||
data.shell_data
|
// Check the role of the surface, this can be either xdg_toplevel
|
||||||
|
// or xdg_popup. If none of the role matches the xdg_surface has no role set
|
||||||
|
// which is a protocol error.
|
||||||
|
let surface = &data.wl_surface;
|
||||||
|
|
||||||
|
if !data.shell_data.compositor_token.has_a_role(surface) {
|
||||||
|
data.shell.as_ref().post_error(
|
||||||
|
zxdg_surface_v6::Error::NotConstructed as u32,
|
||||||
|
"xdg_surface must have a role.".into(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the next window geometry here, the geometry will be moved from
|
||||||
|
// next to the current geometry on a commit. This has to be done currently
|
||||||
|
// in anvil as the whole commit logic is implemented there until a proper
|
||||||
|
// abstraction has been found to handle commits within roles. This also
|
||||||
|
// ensures that a commit for a xdg_surface follows the rules for subsurfaces.
|
||||||
|
let has_wrong_role = data
|
||||||
|
.shell_data
|
||||||
.compositor_token
|
.compositor_token
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
.with_xdg_role(surface, |role| {
|
||||||
data.window_geometry = Some(Rectangle { x, y, width, height });
|
role.set_window_geometry(Rectangle { x, y, width, height })
|
||||||
})
|
})
|
||||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
.is_err();
|
||||||
|
|
||||||
|
if has_wrong_role {
|
||||||
|
data.shell.as_ref().post_error(
|
||||||
|
zxdg_shell_v6::Error::Role as u32,
|
||||||
|
"xdg_surface must have a role of xdg_toplevel or xdg_popup.".into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
zxdg_surface_v6::Request::AckConfigure { serial } => {
|
zxdg_surface_v6::Request::AckConfigure { serial } => {
|
||||||
data.shell_data
|
let serial = Serial::from(serial);
|
||||||
|
let surface = &data.wl_surface;
|
||||||
|
|
||||||
|
// Check the role of the surface, this can be either xdg_toplevel
|
||||||
|
// or xdg_popup. If none of the role matches the xdg_surface has no role set
|
||||||
|
// which is a protocol error.
|
||||||
|
if !data.shell_data.compositor_token.has_a_role(surface) {
|
||||||
|
data.shell.as_ref().post_error(
|
||||||
|
zxdg_surface_v6::Error::NotConstructed as u32,
|
||||||
|
"xdg_surface must have a role.".into(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the correct configure state for the provided serial
|
||||||
|
// discard all configure states that are older than the provided
|
||||||
|
// serial.
|
||||||
|
// If no matching serial can be found raise a protocol error
|
||||||
|
//
|
||||||
|
// Invoke the user impl with the found configuration
|
||||||
|
// This has to include the serial and the role specific data.
|
||||||
|
// - For xdg_popup there is no data.
|
||||||
|
// - For xdg_toplevel send the state data including
|
||||||
|
// width, height, min/max size, maximized, fullscreen, resizing, activated
|
||||||
|
//
|
||||||
|
// This can be used to integrate custom protocol extensions
|
||||||
|
//
|
||||||
|
let configure = match data
|
||||||
|
.shell_data
|
||||||
.compositor_token
|
.compositor_token
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |role_data| {
|
.with_xdg_role(surface, |role| role.ack_configure(serial))
|
||||||
let mut found = false;
|
{
|
||||||
role_data.pending_configures.retain(|&s| {
|
Ok(Ok(configure)) => configure,
|
||||||
if s == serial {
|
Ok(Err(ConfigureError::SerialNotFound(serial))) => {
|
||||||
found = true;
|
data.shell.as_ref().post_error(
|
||||||
}
|
zxdg_shell_v6::Error::InvalidSurfaceState as u32,
|
||||||
s > serial
|
format!("wrong configure serial: {}", <u32>::from(serial)),
|
||||||
});
|
);
|
||||||
if !found {
|
return;
|
||||||
// client responded to a non-existing configure
|
}
|
||||||
data.shell.as_ref().post_error(
|
Err(_) => {
|
||||||
zxdg_shell_v6::Error::InvalidSurfaceState as u32,
|
data.shell.as_ref().post_error(
|
||||||
format!("Wrong configure serial: {}", serial),
|
zxdg_shell_v6::Error::Role as u32,
|
||||||
);
|
"xdg_surface must have a role of xdg_toplevel or xdg_popup.".into(),
|
||||||
}
|
);
|
||||||
role_data.configured = true;
|
return;
|
||||||
})
|
}
|
||||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
};
|
||||||
|
|
||||||
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);
|
|
||||||
(&mut *user_impl)(XdgRequest::AckConfigure {
|
(&mut *user_impl)(XdgRequest::AckConfigure {
|
||||||
surface: data.wl_surface.clone(),
|
surface: surface.clone(),
|
||||||
serial,
|
configure,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
@ -385,38 +464,62 @@ pub struct ShellSurfaceUserData<R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility functions allowing to factor out a lot of the upcoming logic
|
// Utility functions allowing to factor out a lot of the upcoming logic
|
||||||
fn with_surface_toplevel_data<R, F>(toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, f: F)
|
fn with_surface_toplevel_role_data<R, F, T>(
|
||||||
|
shell_data: &ShellData<R>,
|
||||||
|
toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6,
|
||||||
|
f: F,
|
||||||
|
) -> T
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||||
F: FnOnce(&mut ToplevelState),
|
F: FnOnce(&mut XdgToplevelSurfaceRoleAttributes) -> T,
|
||||||
{
|
{
|
||||||
let data = toplevel
|
let data = toplevel
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData<R>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
data.shell_data
|
shell_data
|
||||||
.compositor_token
|
.compositor_token
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| match data.pending_state {
|
.with_role_data::<XdgToplevelSurfaceRole, _, _>(&data.wl_surface, |role| {
|
||||||
XdgSurfacePendingState::Toplevel(ref mut toplevel_data) => f(toplevel_data),
|
let attributes = role
|
||||||
_ => unreachable!(),
|
.as_mut()
|
||||||
|
.expect("xdg_toplevel exists but role has been destroyed?!");
|
||||||
|
f(attributes)
|
||||||
})
|
})
|
||||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
.expect("xdg_toplevel exists but surface has not shell_surface role?!")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_surface_toplevel_client_pending<R, F, T>(
|
||||||
|
shell_data: &ShellData<R>,
|
||||||
|
toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6,
|
||||||
|
f: F,
|
||||||
|
) -> T
|
||||||
|
where
|
||||||
|
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||||
|
F: FnOnce(&mut ToplevelClientPending) -> T,
|
||||||
|
{
|
||||||
|
with_surface_toplevel_role_data(shell_data, toplevel, |data| {
|
||||||
|
if data.client_pending.is_none() {
|
||||||
|
data.client_pending = Some(Default::default());
|
||||||
|
}
|
||||||
|
f(&mut data.client_pending.as_mut().unwrap())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_toplevel_configure<R>(resource: &zxdg_toplevel_v6::ZxdgToplevelV6, configure: ToplevelConfigure)
|
pub fn send_toplevel_configure<R>(resource: &zxdg_toplevel_v6::ZxdgToplevelV6, configure: ToplevelConfigure)
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = resource
|
let data = resource
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData<R>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (width, height) = configure.size.unwrap_or((0, 0));
|
|
||||||
|
let (width, height) = configure.state.size.unwrap_or((0, 0));
|
||||||
// convert the Vec<State> (which is really a Vec<u32>) into Vec<u8>
|
// convert the Vec<State> (which is really a Vec<u32>) into Vec<u8>
|
||||||
let states = {
|
let states = {
|
||||||
let mut states = configure.states;
|
let mut states: Vec<xdg_toplevel::State> = configure.state.states.into();
|
||||||
let ptr = states.as_mut_ptr();
|
let ptr = states.as_mut_ptr();
|
||||||
let len = states.len();
|
let len = states.len();
|
||||||
let cap = states.capacity();
|
let cap = states.capacity();
|
||||||
|
@ -424,15 +527,13 @@ where
|
||||||
unsafe { Vec::from_raw_parts(ptr as *mut u8, len * 4, cap * 4) }
|
unsafe { Vec::from_raw_parts(ptr as *mut u8, len * 4, cap * 4) }
|
||||||
};
|
};
|
||||||
let serial = configure.serial;
|
let serial = configure.serial;
|
||||||
|
|
||||||
|
// Send the toplevel configure
|
||||||
resource.configure(width, height, states);
|
resource.configure(width, height, states);
|
||||||
|
|
||||||
|
// Send the base xdg_surface configure event to mark
|
||||||
|
// The configure as finished
|
||||||
data.xdg_surface.configure(serial.into());
|
data.xdg_surface.configure(serial.into());
|
||||||
// Add the configure as pending
|
|
||||||
data.shell_data
|
|
||||||
.compositor_token
|
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
|
||||||
data.pending_configures.push(serial.into())
|
|
||||||
})
|
|
||||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_toplevel_handle<R: 'static>(
|
fn make_toplevel_handle<R: 'static>(
|
||||||
|
@ -452,7 +553,7 @@ fn make_toplevel_handle<R: 'static>(
|
||||||
|
|
||||||
fn toplevel_implementation<R>(request: zxdg_toplevel_v6::Request, toplevel: zxdg_toplevel_v6::ZxdgToplevelV6)
|
fn toplevel_implementation<R>(request: zxdg_toplevel_v6::Request, toplevel: zxdg_toplevel_v6::ZxdgToplevelV6)
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = toplevel
|
let data = toplevel
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -464,8 +565,8 @@ where
|
||||||
// all it done by the destructor
|
// all it done by the destructor
|
||||||
}
|
}
|
||||||
zxdg_toplevel_v6::Request::SetParent { parent } => {
|
zxdg_toplevel_v6::Request::SetParent { parent } => {
|
||||||
with_surface_toplevel_data::<R, _>(&toplevel, |toplevel_data| {
|
with_surface_toplevel_role_data(&data.shell_data, &toplevel, |data| {
|
||||||
toplevel_data.parent = parent.map(|toplevel_surface_parent| {
|
data.parent = parent.map(|toplevel_surface_parent| {
|
||||||
let parent_data = toplevel_surface_parent
|
let parent_data = toplevel_surface_parent
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
|
@ -476,13 +577,15 @@ where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
zxdg_toplevel_v6::Request::SetTitle { title } => {
|
zxdg_toplevel_v6::Request::SetTitle { title } => {
|
||||||
with_surface_toplevel_data::<R, _>(&toplevel, |toplevel_data| {
|
// Title is not double buffered, we can set it directly
|
||||||
toplevel_data.title = title;
|
with_surface_toplevel_role_data(&data.shell_data, &toplevel, |data| {
|
||||||
|
data.title = Some(title);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
zxdg_toplevel_v6::Request::SetAppId { app_id } => {
|
zxdg_toplevel_v6::Request::SetAppId { app_id } => {
|
||||||
with_surface_toplevel_data::<R, _>(&toplevel, |toplevel_data| {
|
// AppId is not double buffered, we can set it directly
|
||||||
toplevel_data.app_id = app_id;
|
with_surface_toplevel_role_data(&data.shell_data, &toplevel, |role| {
|
||||||
|
role.app_id = Some(app_id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
zxdg_toplevel_v6::Request::ShowWindowMenu { seat, serial, x, y } => {
|
zxdg_toplevel_v6::Request::ShowWindowMenu { seat, serial, x, y } => {
|
||||||
|
@ -520,13 +623,13 @@ where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
zxdg_toplevel_v6::Request::SetMaxSize { width, height } => {
|
zxdg_toplevel_v6::Request::SetMaxSize { width, height } => {
|
||||||
with_surface_toplevel_data::<R, _>(&toplevel, |toplevel_data| {
|
with_surface_toplevel_client_pending(&data.shell_data, &toplevel, |toplevel_data| {
|
||||||
toplevel_data.max_size = (width, height);
|
toplevel_data.max_size = Some((width, height));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
zxdg_toplevel_v6::Request::SetMinSize { width, height } => {
|
zxdg_toplevel_v6::Request::SetMinSize { width, height } => {
|
||||||
with_surface_toplevel_data::<R, _>(&toplevel, |toplevel_data| {
|
with_surface_toplevel_client_pending(&data.shell_data, &toplevel, |toplevel_data| {
|
||||||
toplevel_data.min_size = (width, height);
|
toplevel_data.min_size = Some((width, height));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
zxdg_toplevel_v6::Request::SetMaximized => {
|
zxdg_toplevel_v6::Request::SetMaximized => {
|
||||||
|
@ -553,6 +656,8 @@ where
|
||||||
(&mut *user_impl)(XdgRequest::UnFullscreen { surface: handle });
|
(&mut *user_impl)(XdgRequest::UnFullscreen { surface: handle });
|
||||||
}
|
}
|
||||||
zxdg_toplevel_v6::Request::SetMinimized => {
|
zxdg_toplevel_v6::Request::SetMinimized => {
|
||||||
|
// This has to be handled by the compositor, may not be
|
||||||
|
// 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 });
|
||||||
|
@ -563,7 +668,7 @@ where
|
||||||
|
|
||||||
fn destroy_toplevel<R>(toplevel: zxdg_toplevel_v6::ZxdgToplevelV6)
|
fn destroy_toplevel<R>(toplevel: zxdg_toplevel_v6::ZxdgToplevelV6)
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = toplevel
|
let data = toplevel
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -577,9 +682,8 @@ where
|
||||||
} else {
|
} else {
|
||||||
data.shell_data
|
data.shell_data
|
||||||
.compositor_token
|
.compositor_token
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
.with_role_data(&data.wl_surface, |role_data| {
|
||||||
data.pending_state = XdgSurfacePendingState::None;
|
*role_data = XdgToplevelSurfaceRole::None;
|
||||||
data.configured = false;
|
|
||||||
})
|
})
|
||||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
||||||
}
|
}
|
||||||
|
@ -598,25 +702,23 @@ where
|
||||||
|
|
||||||
pub(crate) fn send_popup_configure<R>(resource: &zxdg_popup_v6::ZxdgPopupV6, configure: PopupConfigure)
|
pub(crate) fn send_popup_configure<R>(resource: &zxdg_popup_v6::ZxdgPopupV6, configure: PopupConfigure)
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = resource
|
let data = resource
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData<R>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (x, y) = configure.position;
|
|
||||||
let (width, height) = configure.size;
|
|
||||||
let serial = configure.serial;
|
let serial = configure.serial;
|
||||||
resource.configure(x, y, width, height);
|
let geometry = configure.state.geometry;
|
||||||
|
|
||||||
|
// Send the popup configure
|
||||||
|
resource.configure(geometry.x, geometry.y, geometry.width, geometry.height);
|
||||||
|
|
||||||
|
// Send the base xdg_surface configure event to mark
|
||||||
|
// the configure as finished
|
||||||
data.xdg_surface.configure(serial.into());
|
data.xdg_surface.configure(serial.into());
|
||||||
// Add the configure as pending
|
|
||||||
data.shell_data
|
|
||||||
.compositor_token
|
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
|
||||||
data.pending_configures.push(serial.into())
|
|
||||||
})
|
|
||||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_popup_handle<R: 'static>(resource: &zxdg_popup_v6::ZxdgPopupV6) -> super::PopupSurface<R> {
|
fn make_popup_handle<R: 'static>(resource: &zxdg_popup_v6::ZxdgPopupV6) -> super::PopupSurface<R> {
|
||||||
|
@ -634,7 +736,7 @@ fn make_popup_handle<R: 'static>(resource: &zxdg_popup_v6::ZxdgPopupV6) -> super
|
||||||
|
|
||||||
fn popup_implementation<R>(request: zxdg_popup_v6::Request, popup: zxdg_popup_v6::ZxdgPopupV6)
|
fn popup_implementation<R>(request: zxdg_popup_v6::Request, popup: zxdg_popup_v6::ZxdgPopupV6)
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = popup
|
let data = popup
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -661,7 +763,7 @@ where
|
||||||
|
|
||||||
fn destroy_popup<R>(popup: zxdg_popup_v6::ZxdgPopupV6)
|
fn destroy_popup<R>(popup: zxdg_popup_v6::ZxdgPopupV6)
|
||||||
where
|
where
|
||||||
R: Role<XdgSurfaceRole> + 'static,
|
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||||
{
|
{
|
||||||
let data = popup
|
let data = popup
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -675,9 +777,8 @@ where
|
||||||
} else {
|
} else {
|
||||||
data.shell_data
|
data.shell_data
|
||||||
.compositor_token
|
.compositor_token
|
||||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
.with_role_data(&data.wl_surface, |role_data| {
|
||||||
data.pending_state = XdgSurfacePendingState::None;
|
*role_data = XdgPopupSurfaceRole::None;
|
||||||
data.configured = false;
|
|
||||||
})
|
})
|
||||||
.expect("xdg_popup exists but surface has not shell_surface role?!");
|
.expect("xdg_popup exists but surface has not shell_surface role?!");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue