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,
|
||||
},
|
||||
xdg::{
|
||||
xdg_shell_init, PopupConfigure, ShellState as XdgShellState, ToplevelConfigure, XdgRequest,
|
||||
XdgSurfacePendingState, XdgSurfaceRole,
|
||||
xdg_shell_init, Configure, ShellState as XdgShellState, XdgPopupSurfaceRole, XdgRequest,
|
||||
XdgToplevelSurfaceRole,
|
||||
},
|
||||
},
|
||||
Serial,
|
||||
|
@ -38,24 +38,17 @@ use smithay::{
|
|||
|
||||
use crate::{
|
||||
state::AnvilState,
|
||||
window_map::{Kind as SurfaceKind, WindowMap},
|
||||
window_map::{Kind as SurfaceKind, PopupKind, WindowMap},
|
||||
};
|
||||
|
||||
#[cfg(feature = "xwayland")]
|
||||
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 =>
|
||||
[ XdgSurface, XdgSurfaceRole ]
|
||||
[ ShellSurface, ShellSurfaceRole]
|
||||
[ DnDIcon, DnDIconRole ]
|
||||
[ CursorImage, CursorImageRole ]
|
||||
);
|
||||
#[cfg(feature = "xwayland")]
|
||||
define_roles!(Roles =>
|
||||
[ XdgSurface, XdgSurfaceRole ]
|
||||
[ XdgToplevelSurface, XdgToplevelSurfaceRole ]
|
||||
[ XdgPopupSurface, XdgPopupSurfaceRole ]
|
||||
[ ShellSurface, ShellSurfaceRole]
|
||||
#[cfg(feature = "xwayland")]
|
||||
[ X11Surface, X11SurfaceRole ]
|
||||
[ DnDIcon, DnDIconRole ]
|
||||
[ CursorImage, CursorImageRole ]
|
||||
|
@ -172,7 +165,7 @@ impl PointerGrab for ResizeSurfaceGrab {
|
|||
_handle: &mut PointerInnerHandle<'_>,
|
||||
location: (f64, f64),
|
||||
_focus: Option<(wl_surface::WlSurface, (f64, f64))>,
|
||||
serial: Serial,
|
||||
_serial: Serial,
|
||||
_time: u32,
|
||||
) {
|
||||
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);
|
||||
|
||||
match &self.toplevel {
|
||||
SurfaceKind::Xdg(xdg) => xdg.send_configure(ToplevelConfigure {
|
||||
size: Some(self.last_window_size),
|
||||
states: vec![xdg_toplevel::State::Resizing],
|
||||
serial,
|
||||
}),
|
||||
SurfaceKind::Xdg(xdg) => {
|
||||
if xdg
|
||||
.with_pending_state(|state| {
|
||||
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(
|
||||
(self.last_window_size.0 as u32, self.last_window_size.1 as u32),
|
||||
self.edges.into(),
|
||||
|
@ -256,12 +255,15 @@ impl PointerGrab for ResizeSurfaceGrab {
|
|||
handle.unset_grab(serial, time);
|
||||
|
||||
if let SurfaceKind::Xdg(xdg) = &self.toplevel {
|
||||
// Send the final configure without the resizing state.
|
||||
xdg.send_configure(ToplevelConfigure {
|
||||
size: Some(self.last_window_size),
|
||||
states: vec![],
|
||||
serial,
|
||||
});
|
||||
if xdg
|
||||
.with_pending_state(|state| {
|
||||
state.states.unset(xdg_toplevel::State::Resizing);
|
||||
state.size = Some(self.last_window_size);
|
||||
})
|
||||
.is_ok()
|
||||
{
|
||||
xdg.send_configure();
|
||||
}
|
||||
|
||||
self.ctoken
|
||||
.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 x = range.sample(&mut rng);
|
||||
let y = range.sample(&mut rng);
|
||||
surface.send_configure(ToplevelConfigure {
|
||||
size: None,
|
||||
states: vec![],
|
||||
serial: Serial::from(42),
|
||||
});
|
||||
// Do not send a configure here, the initial configure
|
||||
// of a xdg_surface has to be sent during the commit if
|
||||
// the surface is not already configured
|
||||
xdg_window_map
|
||||
.borrow_mut()
|
||||
.insert(SurfaceKind::Xdg(surface), (x, y));
|
||||
}
|
||||
XdgRequest::NewPopup { surface } => surface.send_configure(PopupConfigure {
|
||||
size: (10, 10),
|
||||
position: (10, 10),
|
||||
serial: Serial::from(42),
|
||||
}),
|
||||
XdgRequest::NewPopup { surface } => {
|
||||
// Do not send a configure here, the initial configure
|
||||
// of a xdg_surface has to be sent during the commit if
|
||||
// the surface is not already configured
|
||||
xdg_window_map.borrow_mut().insert_popup(PopupKind::Xdg(surface));
|
||||
}
|
||||
XdgRequest::Move {
|
||||
surface,
|
||||
seat,
|
||||
|
@ -462,38 +463,95 @@ pub fn init_shell<Backend: 'static>(display: &mut Display, log: ::slog::Logger)
|
|||
|
||||
pointer.set_grab(grab, serial);
|
||||
}
|
||||
XdgRequest::AckConfigure { surface, .. } => {
|
||||
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);
|
||||
XdgRequest::AckConfigure {
|
||||
surface, configure, ..
|
||||
} => {
|
||||
if let Configure::Toplevel(configure) = configure {
|
||||
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
|
||||
});
|
||||
|
||||
if let Some(serial) = waiting_for_serial {
|
||||
let acked = compositor_token
|
||||
.with_role_data(&surface, |role: &mut XdgSurfaceRole| {
|
||||
!role.pending_configures.contains(&serial.into())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
if acked {
|
||||
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!()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
XdgRequest::Fullscreen { surface, output, .. } => {
|
||||
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::Fullscreen);
|
||||
state.size = Some((800, 600));
|
||||
// TODO: If the provided output is None, use the output where
|
||||
// the toplevel is currently shown
|
||||
state.fullscreen_output = output;
|
||||
})
|
||||
.is_ok()
|
||||
{
|
||||
surface.send_configure();
|
||||
}
|
||||
}
|
||||
XdgRequest::UnFullscreen { surface } => {
|
||||
if surface
|
||||
.with_pending_state(|state| {
|
||||
state.states.unset(xdg_toplevel::State::Fullscreen);
|
||||
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 min_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 {
|
||||
min_size = state.min_size;
|
||||
max_size = state.max_size;
|
||||
|
||||
if token.has_role::<XdgToplevelSurfaceRole>(surface) {
|
||||
if let Some(SurfaceKind::Xdg(xdg)) = window_map.borrow().find(surface) {
|
||||
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
|
||||
.with_role_data(surface, |&mut role: &mut SubsurfaceRole| role)
|
||||
|
|
|
@ -7,7 +7,7 @@ use smithay::{
|
|||
compositor::{roles::Role, CompositorToken, SubsurfaceRole, TraversalAction},
|
||||
shell::{
|
||||
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>
|
||||
where
|
||||
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole> + 'static,
|
||||
R: Role<SubsurfaceRole> + Role<XdgToplevelSurfaceRole> + Role<ShellSurfaceRole> + 'static,
|
||||
{
|
||||
pub fn alive(&self) -> bool {
|
||||
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> {
|
||||
location: (i32, i32),
|
||||
/// A bounding box over this window and its children.
|
||||
|
@ -80,7 +109,7 @@ struct Window<R> {
|
|||
|
||||
impl<R> Window<R>
|
||||
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
|
||||
/// surface.
|
||||
|
@ -203,19 +232,29 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Popup<R> {
|
||||
popup: PopupKind<R>,
|
||||
}
|
||||
|
||||
pub struct WindowMap<R> {
|
||||
ctoken: CompositorToken<R>,
|
||||
windows: Vec<Window<R>>,
|
||||
popups: Vec<Popup<R>>,
|
||||
}
|
||||
|
||||
impl<R> WindowMap<R>
|
||||
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 {
|
||||
WindowMap {
|
||||
ctoken,
|
||||
windows: Vec::new(),
|
||||
popups: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,6 +268,11 @@ where
|
|||
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))> {
|
||||
for w in &self.windows {
|
||||
if let Some(surface) = w.matching(point, self.ctoken) {
|
||||
|
@ -269,6 +313,7 @@ where
|
|||
|
||||
pub fn refresh(&mut self) {
|
||||
self.windows.retain(|w| w.toplevel.alive());
|
||||
self.popups.retain(|p| p.popup.alive());
|
||||
for w in &mut self.windows {
|
||||
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.
|
||||
pub fn location(&self, toplevel: &Kind<R>) -> Option<(i32, i32)> {
|
||||
self.windows
|
||||
|
|
|
@ -15,3 +15,15 @@ impl std::fmt::Display 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
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
pub struct Rectangle {
|
||||
/// horizontal position of the top-left corner of the rectangle, in surface coordinates
|
||||
pub x: i32,
|
||||
|
|
|
@ -186,17 +186,17 @@ macro_rules! define_roles(
|
|||
($enum_name: ident) => {
|
||||
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 =>
|
||||
// add in subsurface role
|
||||
[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 {
|
||||
NoRole,
|
||||
$($role_name($role_data)),*
|
||||
$($(#[$role_attr])* $role_name($role_data)),*
|
||||
}
|
||||
|
||||
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 {
|
||||
fn set_with(&mut self, data: $role_data) -> ::std::result::Result<(), $role_data> {
|
||||
if let $enum_name::NoRole = *self {
|
||||
|
|
|
@ -74,6 +74,8 @@ use wayland_server::{
|
|||
Display, Filter, Global,
|
||||
};
|
||||
|
||||
use super::PingError;
|
||||
|
||||
mod wl_handlers;
|
||||
|
||||
/// Metadata associated with the `wl_surface` role
|
||||
|
@ -83,7 +85,7 @@ pub struct ShellSurfaceRole {
|
|||
pub title: String,
|
||||
/// Class of the surface
|
||||
pub class: String,
|
||||
pending_ping: Serial,
|
||||
pending_ping: Option<Serial>,
|
||||
}
|
||||
|
||||
/// A handle to a shell surface
|
||||
|
@ -139,24 +141,21 @@ where
|
|||
/// 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.
|
||||
pub fn send_ping(&self, serial: Serial) -> Result<(), ()> {
|
||||
pub fn send_ping(&self, serial: Serial) -> Result<(), PingError> {
|
||||
if !self.alive() {
|
||||
return Err(());
|
||||
}
|
||||
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(())
|
||||
return Err(PingError::DeadSurface);
|
||||
}
|
||||
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
|
||||
|
|
|
@ -32,7 +32,7 @@ pub(crate) fn implement_shell<R, Impl>(
|
|||
let role_data = ShellSurfaceRole {
|
||||
title: "".into(),
|
||||
class: "".into(),
|
||||
pending_ping: Serial::from(0),
|
||||
pending_ping: None,
|
||||
};
|
||||
if ctoken.give_role_with(&surface, role_data).is_err() {
|
||||
shell
|
||||
|
@ -102,8 +102,8 @@ where
|
|||
let serial = Serial::from(serial);
|
||||
let valid = ctoken
|
||||
.with_role_data(&data.surface, |data| {
|
||||
if data.pending_ping == serial {
|
||||
data.pending_ping = Serial::from(0);
|
||||
if data.pending_ping == Some(serial) {
|
||||
data.pending_ping = None;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
|
|
@ -14,5 +14,20 @@
|
|||
//! - 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`.
|
||||
|
||||
use super::Serial;
|
||||
use thiserror::Error;
|
||||
|
||||
pub mod legacy;
|
||||
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 crate::wayland::compositor::{roles::*, CompositorToken};
|
||||
use crate::wayland::shell::xdg::{ConfigureError, PopupState};
|
||||
use crate::wayland::Serial;
|
||||
use wayland_protocols::xdg_shell::server::{
|
||||
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 super::{
|
||||
make_shell_client_data, PopupConfigure, PopupKind, PopupState, PositionerState, ShellClient,
|
||||
ShellClientData, ShellData, ToplevelConfigure, ToplevelKind, ToplevelState, XdgRequest,
|
||||
XdgSurfacePendingState, XdgSurfaceRole,
|
||||
make_shell_client_data, PopupConfigure, PopupKind, PositionerState, ShellClient, ShellClientData,
|
||||
ShellData, ToplevelClientPending, ToplevelConfigure, ToplevelKind, XdgPopupSurfaceRole,
|
||||
XdgPopupSurfaceRoleAttributes, XdgRequest, XdgToplevelSurfaceRole, XdgToplevelSurfaceRoleAttributes,
|
||||
};
|
||||
|
||||
pub(crate) fn implement_wm_base<R>(
|
||||
|
@ -20,7 +21,7 @@ pub(crate) fn implement_wm_base<R>(
|
|||
shell_data: &ShellData<R>,
|
||||
) -> xdg_wm_base::XdgWmBase
|
||||
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.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)
|
||||
where
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = shell.as_ref().user_data().get::<ShellUserData<R>>().unwrap();
|
||||
match request {
|
||||
|
@ -66,24 +67,9 @@ where
|
|||
implement_positioner(id);
|
||||
}
|
||||
xdg_wm_base::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(
|
||||
xdg_wm_base::Error::Role as u32,
|
||||
"Surface already has a role.".into(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Do not assign a role to the surface here
|
||||
// xdg_surface is not role, only xdg_toplevel and
|
||||
// xdg_popup are defined as roles
|
||||
id.quick_assign(|surface, req, _data| {
|
||||
xdg_surface_implementation::<R>(req, surface.deref().clone())
|
||||
});
|
||||
|
@ -98,8 +84,8 @@ where
|
|||
let serial = Serial::from(serial);
|
||||
let valid = {
|
||||
let mut guard = data.client_data.lock().unwrap();
|
||||
if guard.pending_ping == serial {
|
||||
guard.pending_ping = Serial::from(0);
|
||||
if guard.pending_ping == Some(serial) {
|
||||
guard.pending_ping = None;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -191,7 +177,7 @@ struct XdgSurfaceUserData<R> {
|
|||
|
||||
fn destroy_surface<R>(surface: xdg_surface::XdgSurface)
|
||||
where
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = surface
|
||||
.as_ref()
|
||||
|
@ -204,24 +190,36 @@ where
|
|||
// disconnecting client), ignore the protocol check.
|
||||
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
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |rdata| {
|
||||
if let XdgSurfacePendingState::None = rdata.pending_state {
|
||||
// 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(),
|
||||
);
|
||||
}
|
||||
.with_role_data(&data.wl_surface, |role: &mut XdgToplevelSurfaceRole| {
|
||||
role.is_some()
|
||||
})
|
||||
.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)
|
||||
where
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = xdg_surface
|
||||
.as_ref()
|
||||
|
@ -233,18 +231,25 @@ where
|
|||
// all is handled by our destructor
|
||||
}
|
||||
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
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
||||
data.pending_state = XdgSurfacePendingState::Toplevel(ToplevelState {
|
||||
parent: None,
|
||||
title: String::new(),
|
||||
app_id: String::new(),
|
||||
min_size: (0, 0),
|
||||
max_size: (0, 0),
|
||||
});
|
||||
})
|
||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
.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(|toplevel, req, _data| {
|
||||
toplevel_implementation::<R>(req, toplevel.deref().clone())
|
||||
});
|
||||
|
@ -276,7 +281,9 @@ where
|
|||
.as_ref()
|
||||
.user_data()
|
||||
.get::<RefCell<PositionerState>>()
|
||||
.unwrap();
|
||||
.unwrap()
|
||||
.borrow()
|
||||
.clone();
|
||||
|
||||
let parent_surface = parent.map(|parent| {
|
||||
let parent_data = parent
|
||||
|
@ -286,16 +293,34 @@ where
|
|||
.unwrap();
|
||||
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
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
||||
data.pending_state = XdgSurfacePendingState::Popup(PopupState {
|
||||
parent: parent_surface,
|
||||
positioner: positioner_data.borrow().clone(),
|
||||
});
|
||||
})
|
||||
.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()));
|
||||
.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(|popup, req, _data| xdg_popup_implementation::<R>(req, popup.deref().clone()));
|
||||
id.assign_destructor(Filter::new(|popup, _, _data| destroy_popup::<R>(popup)));
|
||||
id.as_ref().user_data().set(|| ShellSurfaceUserData {
|
||||
shell_data: data.shell_data.clone(),
|
||||
|
@ -316,40 +341,93 @@ where
|
|||
(&mut *user_impl)(XdgRequest::NewPopup { surface: handle });
|
||||
}
|
||||
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
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
||||
data.window_geometry = Some(Rectangle { x, y, width, height });
|
||||
.with_xdg_role(surface, |role| {
|
||||
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 } => {
|
||||
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
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |role_data| {
|
||||
let mut found = false;
|
||||
role_data.pending_configures.retain(|&s| {
|
||||
if s == serial {
|
||||
found = true;
|
||||
}
|
||||
s > serial
|
||||
});
|
||||
if !found {
|
||||
// client responded to a non-existing configure
|
||||
data.wm_base.as_ref().post_error(
|
||||
xdg_wm_base::Error::InvalidSurfaceState as u32,
|
||||
format!("Wrong configure serial: {}", serial),
|
||||
);
|
||||
}
|
||||
role_data.configured = true;
|
||||
})
|
||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
.with_xdg_role(surface, |role| role.ack_configure(serial))
|
||||
{
|
||||
Ok(Ok(configure)) => configure,
|
||||
Ok(Err(ConfigureError::SerialNotFound(serial))) => {
|
||||
data.wm_base.as_ref().post_error(
|
||||
xdg_wm_base::Error::InvalidSurfaceState as u32,
|
||||
format!("wrong configure serial: {}", <u32>::from(serial)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
Err(_) => {
|
||||
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(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut user_impl = data.shell_data.user_impl.borrow_mut();
|
||||
let serial = Serial::from(serial);
|
||||
(&mut *user_impl)(XdgRequest::AckConfigure {
|
||||
surface: data.wl_surface.clone(),
|
||||
serial,
|
||||
surface: surface.clone(),
|
||||
configure,
|
||||
});
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
@ -368,38 +446,62 @@ pub(crate) struct ShellSurfaceUserData<R> {
|
|||
}
|
||||
|
||||
// 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
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
F: FnOnce(&mut ToplevelState),
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
F: FnOnce(&mut XdgToplevelSurfaceRoleAttributes) -> T,
|
||||
{
|
||||
let toplevel_data = toplevel
|
||||
let data = toplevel
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.unwrap();
|
||||
shell_data
|
||||
.compositor_token
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&toplevel_data.wl_surface, |data| match data.pending_state {
|
||||
XdgSurfacePendingState::Toplevel(ref mut toplevel_data) => f(toplevel_data),
|
||||
_ => unreachable!(),
|
||||
.with_role_data::<XdgToplevelSurfaceRole, _, _>(&data.wl_surface, |role| {
|
||||
let attributes = role
|
||||
.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)
|
||||
where
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
{
|
||||
let data = resource
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.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>
|
||||
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 len = states.len();
|
||||
let cap = states.capacity();
|
||||
|
@ -407,15 +509,13 @@ where
|
|||
unsafe { Vec::from_raw_parts(ptr as *mut u8, len * 4, cap * 4) }
|
||||
};
|
||||
let serial = configure.serial;
|
||||
|
||||
// Send the toplevel configure
|
||||
resource.configure(width, height, states);
|
||||
|
||||
// Send the base xdg_surface configure event to mark
|
||||
// The configure as finished
|
||||
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> {
|
||||
|
@ -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)
|
||||
where
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
{
|
||||
let data = toplevel
|
||||
.as_ref()
|
||||
|
@ -445,8 +545,9 @@ where
|
|||
// all it done by the destructor
|
||||
}
|
||||
xdg_toplevel::Request::SetParent { parent } => {
|
||||
with_surface_toplevel_data(&data.shell_data, &toplevel, |toplevel_data| {
|
||||
toplevel_data.parent = parent.map(|toplevel_surface_parent| {
|
||||
// Parent is not double buffered, we can set it directly
|
||||
with_surface_toplevel_role_data(&data.shell_data, &toplevel, |data| {
|
||||
data.parent = parent.map(|toplevel_surface_parent| {
|
||||
toplevel_surface_parent
|
||||
.as_ref()
|
||||
.user_data()
|
||||
|
@ -458,16 +559,19 @@ where
|
|||
});
|
||||
}
|
||||
xdg_toplevel::Request::SetTitle { title } => {
|
||||
with_surface_toplevel_data(&data.shell_data, &toplevel, |toplevel_data| {
|
||||
toplevel_data.title = title;
|
||||
// Title is not double buffered, we can set it directly
|
||||
with_surface_toplevel_role_data(&data.shell_data, &toplevel, |data| {
|
||||
data.title = Some(title);
|
||||
});
|
||||
}
|
||||
xdg_toplevel::Request::SetAppId { app_id } => {
|
||||
with_surface_toplevel_data(&data.shell_data, &toplevel, |toplevel_data| {
|
||||
toplevel_data.app_id = app_id;
|
||||
// AppId is not double buffered, we can set it directly
|
||||
with_surface_toplevel_role_data(&data.shell_data, &toplevel, |role| {
|
||||
role.app_id = Some(app_id);
|
||||
});
|
||||
}
|
||||
xdg_toplevel::Request::ShowWindowMenu { seat, serial, x, y } => {
|
||||
// This has to be handled by the compositor
|
||||
let handle = make_toplevel_handle(&toplevel);
|
||||
let serial = Serial::from(serial);
|
||||
let mut user_impl = data.shell_data.user_impl.borrow_mut();
|
||||
|
@ -479,6 +583,7 @@ where
|
|||
});
|
||||
}
|
||||
xdg_toplevel::Request::Move { seat, serial } => {
|
||||
// This has to be handled by the compositor
|
||||
let handle = make_toplevel_handle(&toplevel);
|
||||
let serial = Serial::from(serial);
|
||||
let mut user_impl = data.shell_data.user_impl.borrow_mut();
|
||||
|
@ -489,6 +594,7 @@ where
|
|||
});
|
||||
}
|
||||
xdg_toplevel::Request::Resize { seat, serial, edges } => {
|
||||
// This has to be handled by the compositor
|
||||
let handle = make_toplevel_handle(&toplevel);
|
||||
let mut user_impl = data.shell_data.user_impl.borrow_mut();
|
||||
let serial = Serial::from(serial);
|
||||
|
@ -500,13 +606,13 @@ where
|
|||
});
|
||||
}
|
||||
xdg_toplevel::Request::SetMaxSize { width, height } => {
|
||||
with_surface_toplevel_data(&data.shell_data, &toplevel, |toplevel_data| {
|
||||
toplevel_data.max_size = (width, height);
|
||||
with_surface_toplevel_client_pending(&data.shell_data, &toplevel, |toplevel_data| {
|
||||
toplevel_data.max_size = Some((width, height));
|
||||
});
|
||||
}
|
||||
xdg_toplevel::Request::SetMinSize { width, height } => {
|
||||
with_surface_toplevel_data(&data.shell_data, &toplevel, |toplevel_data| {
|
||||
toplevel_data.min_size = (width, height);
|
||||
with_surface_toplevel_client_pending(&data.shell_data, &toplevel, |toplevel_data| {
|
||||
toplevel_data.min_size = Some((width, height));
|
||||
});
|
||||
}
|
||||
xdg_toplevel::Request::SetMaximized => {
|
||||
|
@ -533,6 +639,8 @@ where
|
|||
(&mut *user_impl)(XdgRequest::UnFullscreen { surface: handle });
|
||||
}
|
||||
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 mut user_impl = data.shell_data.user_impl.borrow_mut();
|
||||
(&mut *user_impl)(XdgRequest::Minimize { surface: handle });
|
||||
|
@ -543,7 +651,7 @@ where
|
|||
|
||||
fn destroy_toplevel<R>(toplevel: xdg_toplevel::XdgToplevel)
|
||||
where
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
{
|
||||
let data = toplevel
|
||||
.as_ref()
|
||||
|
@ -557,9 +665,8 @@ where
|
|||
} else {
|
||||
data.shell_data
|
||||
.compositor_token
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
||||
data.pending_state = XdgSurfacePendingState::None;
|
||||
data.configured = false;
|
||||
.with_role_data(&data.wl_surface, |role_data| {
|
||||
*role_data = XdgToplevelSurfaceRole::None;
|
||||
})
|
||||
.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)
|
||||
where
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = resource
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.unwrap();
|
||||
let (x, y) = configure.position;
|
||||
let (width, height) = configure.size;
|
||||
|
||||
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());
|
||||
// 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> {
|
||||
|
@ -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
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = popup
|
||||
.as_ref()
|
||||
|
@ -641,7 +746,7 @@ where
|
|||
|
||||
fn destroy_popup<R>(popup: xdg_popup::XdgPopup)
|
||||
where
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = popup
|
||||
.as_ref()
|
||||
|
@ -655,9 +760,8 @@ where
|
|||
} else {
|
||||
data.shell_data
|
||||
.compositor_token
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
||||
data.pending_state = XdgSurfacePendingState::None;
|
||||
data.configured = false;
|
||||
.with_role_data(&data.wl_surface, |role_data| {
|
||||
*role_data = XdgPopupSurfaceRole::None;
|
||||
})
|
||||
.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 crate::wayland::compositor::{roles::*, CompositorToken};
|
||||
use crate::wayland::shell::xdg::{ConfigureError, PopupState};
|
||||
use crate::wayland::Serial;
|
||||
use wayland_protocols::{
|
||||
unstable::xdg_shell::v6::server::{
|
||||
|
@ -13,9 +14,9 @@ use wayland_server::{protocol::wl_surface, Filter, Main};
|
|||
use crate::utils::Rectangle;
|
||||
|
||||
use super::{
|
||||
make_shell_client_data, PopupConfigure, PopupKind, PopupState, PositionerState, ShellClient,
|
||||
ShellClientData, ShellData, ToplevelConfigure, ToplevelKind, ToplevelState, XdgRequest,
|
||||
XdgSurfacePendingState, XdgSurfaceRole,
|
||||
make_shell_client_data, PopupConfigure, PopupKind, PositionerState, ShellClient, ShellClientData,
|
||||
ShellData, ToplevelClientPending, ToplevelConfigure, ToplevelKind, XdgPopupSurfaceRole,
|
||||
XdgPopupSurfaceRoleAttributes, XdgRequest, XdgToplevelSurfaceRole, XdgToplevelSurfaceRoleAttributes,
|
||||
};
|
||||
|
||||
pub(crate) fn implement_shell<R>(
|
||||
|
@ -23,7 +24,7 @@ pub(crate) fn implement_shell<R>(
|
|||
shell_data: &ShellData<R>,
|
||||
) -> zxdg_shell_v6::ZxdgShellV6
|
||||
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.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)
|
||||
where
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = shell.as_ref().user_data().get::<ShellUserData<R>>().unwrap();
|
||||
match request {
|
||||
|
@ -69,24 +70,6 @@ where
|
|||
implement_positioner(id);
|
||||
}
|
||||
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| {
|
||||
xdg_surface_implementation::<R>(req, surface.deref().clone())
|
||||
});
|
||||
|
@ -98,10 +81,11 @@ where
|
|||
});
|
||||
}
|
||||
zxdg_shell_v6::Request::Pong { serial } => {
|
||||
let serial = Serial::from(serial);
|
||||
let valid = {
|
||||
let mut guard = data.client_data.lock().unwrap();
|
||||
if guard.pending_ping == Serial::from(serial) {
|
||||
guard.pending_ping = Serial::from(0);
|
||||
if guard.pending_ping == Some(serial) {
|
||||
guard.pending_ping = None;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -209,7 +193,7 @@ struct XdgSurfaceUserData<R> {
|
|||
|
||||
fn destroy_surface<R>(surface: zxdg_surface_v6::ZxdgSurfaceV6)
|
||||
where
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = surface
|
||||
.as_ref()
|
||||
|
@ -222,26 +206,38 @@ where
|
|||
// disconnecting client), ignore the protocol check.
|
||||
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
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |rdata| {
|
||||
if let XdgSurfacePendingState::None = rdata.pending_state {
|
||||
// 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(),
|
||||
);
|
||||
}
|
||||
.with_role_data(&data.wl_surface, |role: &mut XdgToplevelSurfaceRole| {
|
||||
role.is_some()
|
||||
})
|
||||
.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>(
|
||||
request: zxdg_surface_v6::Request,
|
||||
xdg_surface: zxdg_surface_v6::ZxdgSurfaceV6,
|
||||
) where
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = xdg_surface
|
||||
.as_ref()
|
||||
|
@ -253,18 +249,25 @@ fn xdg_surface_implementation<R>(
|
|||
// all is handled by our destructor
|
||||
}
|
||||
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
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
||||
data.pending_state = XdgSurfacePendingState::Toplevel(ToplevelState {
|
||||
parent: None,
|
||||
title: String::new(),
|
||||
app_id: String::new(),
|
||||
min_size: (0, 0),
|
||||
max_size: (0, 0),
|
||||
});
|
||||
})
|
||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
.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(|toplevel, req, _data| {
|
||||
toplevel_implementation::<R>(req, toplevel.deref().clone())
|
||||
});
|
||||
|
@ -296,22 +299,45 @@ fn xdg_surface_implementation<R>(
|
|||
.as_ref()
|
||||
.user_data()
|
||||
.get::<RefCell<PositionerState>>()
|
||||
.unwrap();
|
||||
.unwrap()
|
||||
.borrow()
|
||||
.clone();
|
||||
|
||||
let parent_data = parent
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<XdgSurfaceUserData<R>>()
|
||||
.unwrap();
|
||||
data.shell_data
|
||||
let parent_surface = {
|
||||
let parent_data = parent
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<XdgSurfaceUserData<R>>()
|
||||
.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
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
||||
data.pending_state = XdgSurfacePendingState::Popup(PopupState {
|
||||
parent: Some(parent_data.wl_surface.clone()),
|
||||
positioner: positioner_data.borrow().clone(),
|
||||
});
|
||||
})
|
||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
.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(|popup, req, _data| popup_implementation::<R>(req, popup.deref().clone()));
|
||||
id.assign_destructor(Filter::new(|popup, _, _data| destroy_popup::<R>(popup)));
|
||||
id.as_ref().user_data().set(|| ShellSurfaceUserData {
|
||||
|
@ -333,40 +359,93 @@ fn xdg_surface_implementation<R>(
|
|||
(&mut *user_impl)(XdgRequest::NewPopup { surface: handle });
|
||||
}
|
||||
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
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
||||
data.window_geometry = Some(Rectangle { x, y, width, height });
|
||||
.with_xdg_role(surface, |role| {
|
||||
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 } => {
|
||||
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
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |role_data| {
|
||||
let mut found = false;
|
||||
role_data.pending_configures.retain(|&s| {
|
||||
if s == serial {
|
||||
found = true;
|
||||
}
|
||||
s > serial
|
||||
});
|
||||
if !found {
|
||||
// client responded to a non-existing configure
|
||||
data.shell.as_ref().post_error(
|
||||
zxdg_shell_v6::Error::InvalidSurfaceState as u32,
|
||||
format!("Wrong configure serial: {}", serial),
|
||||
);
|
||||
}
|
||||
role_data.configured = true;
|
||||
})
|
||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
.with_xdg_role(surface, |role| role.ack_configure(serial))
|
||||
{
|
||||
Ok(Ok(configure)) => configure,
|
||||
Ok(Err(ConfigureError::SerialNotFound(serial))) => {
|
||||
data.shell.as_ref().post_error(
|
||||
zxdg_shell_v6::Error::InvalidSurfaceState as u32,
|
||||
format!("wrong configure serial: {}", <u32>::from(serial)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
Err(_) => {
|
||||
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(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut user_impl = data.shell_data.user_impl.borrow_mut();
|
||||
let serial = Serial::from(serial);
|
||||
(&mut *user_impl)(XdgRequest::AckConfigure {
|
||||
surface: data.wl_surface.clone(),
|
||||
serial,
|
||||
surface: surface.clone(),
|
||||
configure,
|
||||
});
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
@ -385,38 +464,62 @@ pub struct ShellSurfaceUserData<R> {
|
|||
}
|
||||
|
||||
// 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
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
F: FnOnce(&mut ToplevelState),
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
F: FnOnce(&mut XdgToplevelSurfaceRoleAttributes) -> T,
|
||||
{
|
||||
let data = toplevel
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.unwrap();
|
||||
data.shell_data
|
||||
shell_data
|
||||
.compositor_token
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| match data.pending_state {
|
||||
XdgSurfacePendingState::Toplevel(ref mut toplevel_data) => f(toplevel_data),
|
||||
_ => unreachable!(),
|
||||
.with_role_data::<XdgToplevelSurfaceRole, _, _>(&data.wl_surface, |role| {
|
||||
let attributes = role
|
||||
.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)
|
||||
where
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
{
|
||||
let data = resource
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.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>
|
||||
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 len = states.len();
|
||||
let cap = states.capacity();
|
||||
|
@ -424,15 +527,13 @@ where
|
|||
unsafe { Vec::from_raw_parts(ptr as *mut u8, len * 4, cap * 4) }
|
||||
};
|
||||
let serial = configure.serial;
|
||||
|
||||
// Send the toplevel configure
|
||||
resource.configure(width, height, states);
|
||||
|
||||
// Send the base xdg_surface configure event to mark
|
||||
// The configure as finished
|
||||
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>(
|
||||
|
@ -452,7 +553,7 @@ fn make_toplevel_handle<R: 'static>(
|
|||
|
||||
fn toplevel_implementation<R>(request: zxdg_toplevel_v6::Request, toplevel: zxdg_toplevel_v6::ZxdgToplevelV6)
|
||||
where
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
{
|
||||
let data = toplevel
|
||||
.as_ref()
|
||||
|
@ -464,8 +565,8 @@ where
|
|||
// all it done by the destructor
|
||||
}
|
||||
zxdg_toplevel_v6::Request::SetParent { parent } => {
|
||||
with_surface_toplevel_data::<R, _>(&toplevel, |toplevel_data| {
|
||||
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| {
|
||||
let parent_data = toplevel_surface_parent
|
||||
.as_ref()
|
||||
.user_data()
|
||||
|
@ -476,13 +577,15 @@ where
|
|||
});
|
||||
}
|
||||
zxdg_toplevel_v6::Request::SetTitle { title } => {
|
||||
with_surface_toplevel_data::<R, _>(&toplevel, |toplevel_data| {
|
||||
toplevel_data.title = title;
|
||||
// Title is not double buffered, we can set it directly
|
||||
with_surface_toplevel_role_data(&data.shell_data, &toplevel, |data| {
|
||||
data.title = Some(title);
|
||||
});
|
||||
}
|
||||
zxdg_toplevel_v6::Request::SetAppId { app_id } => {
|
||||
with_surface_toplevel_data::<R, _>(&toplevel, |toplevel_data| {
|
||||
toplevel_data.app_id = app_id;
|
||||
// AppId is not double buffered, we can set it directly
|
||||
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 } => {
|
||||
|
@ -520,13 +623,13 @@ where
|
|||
});
|
||||
}
|
||||
zxdg_toplevel_v6::Request::SetMaxSize { width, height } => {
|
||||
with_surface_toplevel_data::<R, _>(&toplevel, |toplevel_data| {
|
||||
toplevel_data.max_size = (width, height);
|
||||
with_surface_toplevel_client_pending(&data.shell_data, &toplevel, |toplevel_data| {
|
||||
toplevel_data.max_size = Some((width, height));
|
||||
});
|
||||
}
|
||||
zxdg_toplevel_v6::Request::SetMinSize { width, height } => {
|
||||
with_surface_toplevel_data::<R, _>(&toplevel, |toplevel_data| {
|
||||
toplevel_data.min_size = (width, height);
|
||||
with_surface_toplevel_client_pending(&data.shell_data, &toplevel, |toplevel_data| {
|
||||
toplevel_data.min_size = Some((width, height));
|
||||
});
|
||||
}
|
||||
zxdg_toplevel_v6::Request::SetMaximized => {
|
||||
|
@ -553,6 +656,8 @@ where
|
|||
(&mut *user_impl)(XdgRequest::UnFullscreen { surface: handle });
|
||||
}
|
||||
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 mut user_impl = data.shell_data.user_impl.borrow_mut();
|
||||
(&mut *user_impl)(XdgRequest::Minimize { surface: handle });
|
||||
|
@ -563,7 +668,7 @@ where
|
|||
|
||||
fn destroy_toplevel<R>(toplevel: zxdg_toplevel_v6::ZxdgToplevelV6)
|
||||
where
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
{
|
||||
let data = toplevel
|
||||
.as_ref()
|
||||
|
@ -577,9 +682,8 @@ where
|
|||
} else {
|
||||
data.shell_data
|
||||
.compositor_token
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
||||
data.pending_state = XdgSurfacePendingState::None;
|
||||
data.configured = false;
|
||||
.with_role_data(&data.wl_surface, |role_data| {
|
||||
*role_data = XdgToplevelSurfaceRole::None;
|
||||
})
|
||||
.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)
|
||||
where
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = resource
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.unwrap();
|
||||
let (x, y) = configure.position;
|
||||
let (width, height) = configure.size;
|
||||
|
||||
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());
|
||||
// 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> {
|
||||
|
@ -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)
|
||||
where
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = popup
|
||||
.as_ref()
|
||||
|
@ -661,7 +763,7 @@ where
|
|||
|
||||
fn destroy_popup<R>(popup: zxdg_popup_v6::ZxdgPopupV6)
|
||||
where
|
||||
R: Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = popup
|
||||
.as_ref()
|
||||
|
@ -675,9 +777,8 @@ where
|
|||
} else {
|
||||
data.shell_data
|
||||
.compositor_token
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
||||
data.pending_state = XdgSurfacePendingState::None;
|
||||
data.configured = false;
|
||||
.with_role_data(&data.wl_surface, |role_data| {
|
||||
*role_data = XdgPopupSurfaceRole::None;
|
||||
})
|
||||
.expect("xdg_popup exists but surface has not shell_surface role?!");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue