compositor: Introduce transaction state tracking
This changes the state handling logic of wl_surface to automatically track subsurface pending in the form of transactions. The role enum (and the associated type parameter) is no more, and replaced by a general-purpose typemap-like container on surfaces. The new logic is introduced in the files: - `src/wayland/compositor/cache.rs` - `src/wayland/compositor/transaction.rs` The rest of the PR is the fallout of these changes, as well as a few trivial clippy fixes.
This commit is contained in:
parent
736e4e8bec
commit
ad55ab71c9
|
@ -11,11 +11,12 @@ edition = "2018"
|
|||
members = [ "anvil" ]
|
||||
|
||||
[dependencies]
|
||||
appendlist = "1.4"
|
||||
bitflags = "1"
|
||||
calloop = "0.8.0"
|
||||
cgmath = "0.18.0"
|
||||
dbus = { version = "0.9.0", optional = true }
|
||||
libseat= { version = "0.1.1", optional = true }
|
||||
downcast-rs = "1.2.0"
|
||||
drm-fourcc = "^2.1.1"
|
||||
drm = { version = "0.4.0", optional = true }
|
||||
drm-ffi = { version = "0.1.0", optional = true }
|
||||
|
@ -24,6 +25,7 @@ input = { version = "0.5", default-features = false, optional = true }
|
|||
image = { version = "0.23.14", default-features = false, optional = true }
|
||||
lazy_static = "1"
|
||||
libc = "0.2.70"
|
||||
libseat= { version = "0.1.1", optional = true }
|
||||
libloading = "0.7.0"
|
||||
nix = "0.20"
|
||||
slog = "2"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::{cell::RefCell, sync::Mutex};
|
||||
|
||||
use slog::Logger;
|
||||
use smithay::{
|
||||
|
@ -11,13 +11,15 @@ use smithay::{
|
|||
reexports::wayland_server::protocol::{wl_buffer, wl_surface},
|
||||
utils::Rectangle,
|
||||
wayland::{
|
||||
compositor::{roles::Role, Damage, SubsurfaceRole, TraversalAction},
|
||||
data_device::DnDIconRole,
|
||||
seat::CursorImageRole,
|
||||
compositor::{
|
||||
get_role, with_states, with_surface_tree_upward, Damage, SubsurfaceCachedState,
|
||||
SurfaceAttributes, TraversalAction,
|
||||
},
|
||||
seat::CursorImageAttributes,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData};
|
||||
use crate::{shell::SurfaceData, window_map::WindowMap};
|
||||
|
||||
struct BufferTextures<T> {
|
||||
buffer: Option<wl_buffer::WlBuffer>,
|
||||
|
@ -37,7 +39,6 @@ pub fn draw_cursor<R, E, F, T>(
|
|||
frame: &mut F,
|
||||
surface: &wl_surface::WlSurface,
|
||||
(x, y): (i32, i32),
|
||||
token: MyCompositorToken,
|
||||
log: &Logger,
|
||||
) -> Result<(), SwapBuffersError>
|
||||
where
|
||||
|
@ -46,9 +47,21 @@ where
|
|||
E: std::error::Error + Into<SwapBuffersError>,
|
||||
T: Texture + 'static,
|
||||
{
|
||||
let (dx, dy) = match token.with_role_data::<CursorImageRole, _, _>(surface, |data| data.hotspot) {
|
||||
Ok(h) => h,
|
||||
Err(_) => {
|
||||
let ret = with_states(surface, |states| {
|
||||
Some(
|
||||
states
|
||||
.data_map
|
||||
.get::<Mutex<CursorImageAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.hotspot,
|
||||
)
|
||||
})
|
||||
.unwrap_or(None);
|
||||
let (dx, dy) = match ret {
|
||||
Some(h) => h,
|
||||
None => {
|
||||
warn!(
|
||||
log,
|
||||
"Trying to display as a cursor a surface that does not have the CursorImage role."
|
||||
|
@ -56,7 +69,7 @@ where
|
|||
(0, 0)
|
||||
}
|
||||
};
|
||||
draw_surface_tree(renderer, frame, surface, (x - dx, y - dy), token, log)
|
||||
draw_surface_tree(renderer, frame, surface, (x - dx, y - dy), log)
|
||||
}
|
||||
|
||||
fn draw_surface_tree<R, E, F, T>(
|
||||
|
@ -64,7 +77,6 @@ fn draw_surface_tree<R, E, F, T>(
|
|||
frame: &mut F,
|
||||
root: &wl_surface::WlSurface,
|
||||
location: (i32, i32),
|
||||
compositor_token: MyCompositorToken,
|
||||
log: &Logger,
|
||||
) -> Result<(), SwapBuffersError>
|
||||
where
|
||||
|
@ -75,15 +87,16 @@ where
|
|||
{
|
||||
let mut result = Ok(());
|
||||
|
||||
compositor_token.with_surface_tree_upward(
|
||||
with_surface_tree_upward(
|
||||
root,
|
||||
location,
|
||||
|_surface, attributes, role, &(mut x, mut y)| {
|
||||
|_surface, states, &(mut x, mut y)| {
|
||||
// Pull a new buffer if available
|
||||
if let Some(data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
|
||||
if let Some(data) = states.data_map.get::<RefCell<SurfaceData>>() {
|
||||
let mut data = data.borrow_mut();
|
||||
let attributes = states.cached_state.current::<SurfaceAttributes>();
|
||||
if data.texture.is_none() {
|
||||
if let Some(buffer) = data.current_state.buffer.take() {
|
||||
if let Some(buffer) = data.buffer.take() {
|
||||
let damage = attributes
|
||||
.damage
|
||||
.iter()
|
||||
|
@ -94,16 +107,18 @@ where
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
match renderer.import_buffer(&buffer, Some(&attributes), &damage) {
|
||||
match renderer.import_buffer(&buffer, Some(states), &damage) {
|
||||
Some(Ok(m)) => {
|
||||
if let Some(BufferType::Shm) = buffer_type(&buffer) {
|
||||
let texture_buffer = if let Some(BufferType::Shm) = buffer_type(&buffer) {
|
||||
buffer.release();
|
||||
}
|
||||
None
|
||||
} else {
|
||||
Some(buffer)
|
||||
};
|
||||
data.texture = Some(Box::new(BufferTextures {
|
||||
buffer: Some(buffer),
|
||||
buffer: texture_buffer,
|
||||
texture: m,
|
||||
})
|
||||
as Box<dyn std::any::Any + 'static>)
|
||||
}))
|
||||
}
|
||||
Some(Err(err)) => {
|
||||
warn!(log, "Error loading buffer: {:?}", err);
|
||||
|
@ -119,9 +134,10 @@ where
|
|||
// Now, should we be drawn ?
|
||||
if data.texture.is_some() {
|
||||
// if yes, also process the children
|
||||
if Role::<SubsurfaceRole>::has(role) {
|
||||
x += data.current_state.sub_location.0;
|
||||
y += data.current_state.sub_location.1;
|
||||
if states.role == Some("subsurface") {
|
||||
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
||||
x += current.location.0;
|
||||
y += current.location.1;
|
||||
}
|
||||
TraversalAction::DoChildren((x, y))
|
||||
} else {
|
||||
|
@ -133,10 +149,9 @@ where
|
|||
TraversalAction::SkipChildren
|
||||
}
|
||||
},
|
||||
|_surface, attributes, role, &(mut x, mut y)| {
|
||||
if let Some(ref data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
|
||||
|_surface, states, &(mut x, mut y)| {
|
||||
if let Some(ref data) = states.data_map.get::<RefCell<SurfaceData>>() {
|
||||
let mut data = data.borrow_mut();
|
||||
let (sub_x, sub_y) = data.current_state.sub_location;
|
||||
if let Some(texture) = data
|
||||
.texture
|
||||
.as_mut()
|
||||
|
@ -144,9 +159,10 @@ where
|
|||
{
|
||||
// we need to re-extract the subsurface offset, as the previous closure
|
||||
// only passes it to our children
|
||||
if Role::<SubsurfaceRole>::has(role) {
|
||||
x += sub_x;
|
||||
y += sub_y;
|
||||
if states.role == Some("subsurface") {
|
||||
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
||||
x += current.location.0;
|
||||
y += current.location.1;
|
||||
}
|
||||
if let Err(err) = frame.render_texture_at(
|
||||
&texture.texture,
|
||||
|
@ -159,7 +175,7 @@ where
|
|||
}
|
||||
}
|
||||
},
|
||||
|_, _, _, _| true,
|
||||
|_, _, _| true,
|
||||
);
|
||||
|
||||
result
|
||||
|
@ -168,9 +184,8 @@ where
|
|||
pub fn draw_windows<R, E, F, T>(
|
||||
renderer: &mut R,
|
||||
frame: &mut F,
|
||||
window_map: &MyWindowMap,
|
||||
window_map: &WindowMap,
|
||||
output_rect: Option<Rectangle>,
|
||||
compositor_token: MyCompositorToken,
|
||||
log: &::slog::Logger,
|
||||
) -> Result<(), SwapBuffersError>
|
||||
where
|
||||
|
@ -192,9 +207,7 @@ where
|
|||
}
|
||||
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
||||
// this surface is a root of a subsurface tree that needs to be drawn
|
||||
if let Err(err) =
|
||||
draw_surface_tree(renderer, frame, &wl_surface, initial_place, compositor_token, log)
|
||||
{
|
||||
if let Err(err) = draw_surface_tree(renderer, frame, &wl_surface, initial_place, log) {
|
||||
result = Err(err);
|
||||
}
|
||||
}
|
||||
|
@ -208,7 +221,6 @@ pub fn draw_dnd_icon<R, E, F, T>(
|
|||
frame: &mut F,
|
||||
surface: &wl_surface::WlSurface,
|
||||
(x, y): (i32, i32),
|
||||
token: MyCompositorToken,
|
||||
log: &::slog::Logger,
|
||||
) -> Result<(), SwapBuffersError>
|
||||
where
|
||||
|
@ -217,11 +229,11 @@ where
|
|||
E: std::error::Error + Into<SwapBuffersError>,
|
||||
T: Texture + 'static,
|
||||
{
|
||||
if !token.has_role::<DnDIconRole>(surface) {
|
||||
if get_role(surface) != Some("dnd_icon") {
|
||||
warn!(
|
||||
log,
|
||||
"Trying to display as a dnd icon a surface that does not have the DndIcon role."
|
||||
);
|
||||
}
|
||||
draw_surface_tree(renderer, frame, surface, (x, y), token, log)
|
||||
draw_surface_tree(renderer, frame, surface, (x, y), log)
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate slog;
|
||||
#[macro_use(define_roles)]
|
||||
extern crate smithay;
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
|
|
|
@ -9,25 +9,22 @@ use smithay::{
|
|||
reexports::{
|
||||
wayland_protocols::xdg_shell::server::xdg_toplevel,
|
||||
wayland_server::{
|
||||
protocol::{wl_buffer, wl_callback, wl_pointer::ButtonState, wl_shell_surface, wl_surface},
|
||||
protocol::{wl_buffer, wl_pointer::ButtonState, wl_shell_surface, wl_surface},
|
||||
Display,
|
||||
},
|
||||
},
|
||||
utils::Rectangle,
|
||||
wayland::{
|
||||
compositor::{
|
||||
compositor_init, roles::Role, BufferAssignment, CompositorToken, RegionAttributes,
|
||||
SubsurfaceRole, SurfaceEvent, TraversalAction,
|
||||
compositor_init, is_sync_subsurface, with_states, with_surface_tree_upward, BufferAssignment,
|
||||
SurfaceAttributes, TraversalAction,
|
||||
},
|
||||
data_device::DnDIconRole,
|
||||
seat::{AxisFrame, CursorImageRole, GrabStartData, PointerGrab, PointerInnerHandle, Seat},
|
||||
seat::{AxisFrame, GrabStartData, PointerGrab, PointerInnerHandle, Seat},
|
||||
shell::{
|
||||
legacy::{
|
||||
wl_shell_init, ShellRequest, ShellState as WlShellState, ShellSurfaceKind, ShellSurfaceRole,
|
||||
},
|
||||
legacy::{wl_shell_init, ShellRequest, ShellState as WlShellState, ShellSurfaceKind},
|
||||
xdg::{
|
||||
xdg_shell_init, Configure, ShellState as XdgShellState, XdgPopupSurfaceRole, XdgRequest,
|
||||
XdgToplevelSurfaceRole,
|
||||
xdg_shell_init, Configure, ShellState as XdgShellState, SurfaceCachedState, XdgRequest,
|
||||
XdgToplevelSurfaceRoleAttributes,
|
||||
},
|
||||
},
|
||||
Serial,
|
||||
|
@ -39,27 +36,10 @@ use crate::{
|
|||
window_map::{Kind as SurfaceKind, PopupKind, WindowMap},
|
||||
};
|
||||
|
||||
#[cfg(feature = "xwayland")]
|
||||
use crate::xwayland::X11SurfaceRole;
|
||||
|
||||
define_roles!(Roles =>
|
||||
[ XdgToplevelSurface, XdgToplevelSurfaceRole ]
|
||||
[ XdgPopupSurface, XdgPopupSurfaceRole ]
|
||||
[ ShellSurface, ShellSurfaceRole]
|
||||
#[cfg(feature = "xwayland")]
|
||||
[ X11Surface, X11SurfaceRole ]
|
||||
[ DnDIcon, DnDIconRole ]
|
||||
[ CursorImage, CursorImageRole ]
|
||||
);
|
||||
|
||||
pub type MyWindowMap = WindowMap<Roles>;
|
||||
|
||||
pub type MyCompositorToken = CompositorToken<Roles>;
|
||||
|
||||
struct MoveSurfaceGrab {
|
||||
start_data: GrabStartData,
|
||||
window_map: Rc<RefCell<MyWindowMap>>,
|
||||
toplevel: SurfaceKind<Roles>,
|
||||
window_map: Rc<RefCell<WindowMap>>,
|
||||
toplevel: SurfaceKind,
|
||||
initial_window_location: (i32, i32),
|
||||
}
|
||||
|
||||
|
@ -150,8 +130,7 @@ impl From<ResizeEdge> for xdg_toplevel::ResizeEdge {
|
|||
|
||||
struct ResizeSurfaceGrab {
|
||||
start_data: GrabStartData,
|
||||
ctoken: MyCompositorToken,
|
||||
toplevel: SurfaceKind<Roles>,
|
||||
toplevel: SurfaceKind,
|
||||
edges: ResizeEdge,
|
||||
initial_window_size: (i32, i32),
|
||||
last_window_size: (i32, i32),
|
||||
|
@ -197,12 +176,11 @@ impl PointerGrab for ResizeSurfaceGrab {
|
|||
new_window_height = (self.initial_window_size.1 as f64 + dy) as i32;
|
||||
}
|
||||
|
||||
let (min_size, max_size) =
|
||||
self.ctoken
|
||||
.with_surface_data(self.toplevel.get_surface().unwrap(), |attrs| {
|
||||
let data = attrs.user_data.get::<RefCell<SurfaceData>>().unwrap().borrow();
|
||||
let (min_size, max_size) = with_states(self.toplevel.get_surface().unwrap(), |states| {
|
||||
let data = states.cached_state.current::<SurfaceCachedState>();
|
||||
(data.min_size, data.max_size)
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let min_width = min_size.0.max(1);
|
||||
let min_height = min_size.1.max(1);
|
||||
|
@ -224,13 +202,11 @@ impl PointerGrab for ResizeSurfaceGrab {
|
|||
|
||||
match &self.toplevel {
|
||||
SurfaceKind::Xdg(xdg) => {
|
||||
if xdg
|
||||
.with_pending_state(|state| {
|
||||
let ret = xdg.with_pending_state(|state| {
|
||||
state.states.set(xdg_toplevel::State::Resizing);
|
||||
state.size = Some(self.last_window_size);
|
||||
})
|
||||
.is_ok()
|
||||
{
|
||||
});
|
||||
if ret.is_ok() {
|
||||
xdg.send_configure();
|
||||
}
|
||||
}
|
||||
|
@ -264,20 +240,17 @@ impl PointerGrab for ResizeSurfaceGrab {
|
|||
}
|
||||
|
||||
if let SurfaceKind::Xdg(xdg) = &self.toplevel {
|
||||
if xdg
|
||||
.with_pending_state(|state| {
|
||||
let ret = xdg.with_pending_state(|state| {
|
||||
state.states.unset(xdg_toplevel::State::Resizing);
|
||||
state.size = Some(self.last_window_size);
|
||||
})
|
||||
.is_ok()
|
||||
{
|
||||
});
|
||||
if ret.is_ok() {
|
||||
xdg.send_configure();
|
||||
}
|
||||
|
||||
self.ctoken
|
||||
.with_surface_data(self.toplevel.get_surface().unwrap(), |attrs| {
|
||||
let mut data = attrs
|
||||
.user_data
|
||||
with_states(self.toplevel.get_surface().unwrap(), |states| {
|
||||
let mut data = states
|
||||
.data_map
|
||||
.get::<RefCell<SurfaceData>>()
|
||||
.unwrap()
|
||||
.borrow_mut();
|
||||
|
@ -286,12 +259,12 @@ impl PointerGrab for ResizeSurfaceGrab {
|
|||
} else {
|
||||
panic!("invalid resize state: {:?}", data.resize_state);
|
||||
}
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
} else {
|
||||
self.ctoken
|
||||
.with_surface_data(self.toplevel.get_surface().unwrap(), |attrs| {
|
||||
let mut data = attrs
|
||||
.user_data
|
||||
with_states(self.toplevel.get_surface().unwrap(), |states| {
|
||||
let mut data = states
|
||||
.data_map
|
||||
.get::<RefCell<SurfaceData>>()
|
||||
.unwrap()
|
||||
.borrow_mut();
|
||||
|
@ -300,7 +273,8 @@ impl PointerGrab for ResizeSurfaceGrab {
|
|||
} else {
|
||||
panic!("invalid resize state: {:?}", data.resize_state);
|
||||
}
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -316,34 +290,30 @@ impl PointerGrab for ResizeSurfaceGrab {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct ShellHandles {
|
||||
pub token: CompositorToken<Roles>,
|
||||
pub xdg_state: Arc<Mutex<XdgShellState<Roles>>>,
|
||||
pub wl_state: Arc<Mutex<WlShellState<Roles>>>,
|
||||
pub window_map: Rc<RefCell<MyWindowMap>>,
|
||||
pub xdg_state: Arc<Mutex<XdgShellState>>,
|
||||
pub wl_state: Arc<Mutex<WlShellState>>,
|
||||
pub window_map: Rc<RefCell<WindowMap>>,
|
||||
}
|
||||
|
||||
pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logger) -> ShellHandles {
|
||||
// Create the compositor
|
||||
let (compositor_token, _, _) = compositor_init(
|
||||
compositor_init(
|
||||
display,
|
||||
move |request, surface, ctoken, mut ddata| match request {
|
||||
SurfaceEvent::Commit => {
|
||||
move |surface, mut ddata| {
|
||||
let anvil_state = ddata.get::<AnvilState<BackendData>>().unwrap();
|
||||
let window_map = anvil_state.window_map.as_ref();
|
||||
surface_commit(&surface, ctoken, &*window_map)
|
||||
}
|
||||
surface_commit(&surface, &*window_map)
|
||||
},
|
||||
log.clone(),
|
||||
);
|
||||
|
||||
// Init a window map, to track the location of our windows
|
||||
let window_map = Rc::new(RefCell::new(WindowMap::new(compositor_token)));
|
||||
let window_map = Rc::new(RefCell::new(WindowMap::new()));
|
||||
|
||||
// init the xdg_shell
|
||||
let xdg_window_map = window_map.clone();
|
||||
let (xdg_shell_state, _, _) = xdg_shell_init(
|
||||
display,
|
||||
compositor_token,
|
||||
move |shell_event| match shell_event {
|
||||
XdgRequest::NewToplevel { surface } => {
|
||||
// place the window at a random location in the [0;800]x[0;800] square
|
||||
|
@ -441,9 +411,9 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
|||
let geometry = xdg_window_map.borrow().geometry(&toplevel).unwrap();
|
||||
let initial_window_size = (geometry.width, geometry.height);
|
||||
|
||||
compositor_token.with_surface_data(surface.get_surface().unwrap(), move |attrs| {
|
||||
attrs
|
||||
.user_data
|
||||
with_states(surface.get_surface().unwrap(), move |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<RefCell<SurfaceData>>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
|
@ -452,11 +422,11 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
|||
initial_window_location,
|
||||
initial_window_size,
|
||||
});
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let grab = ResizeSurfaceGrab {
|
||||
start_data,
|
||||
ctoken: compositor_token,
|
||||
toplevel,
|
||||
edges: edges.into(),
|
||||
initial_window_size,
|
||||
|
@ -466,18 +436,20 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
|||
pointer.set_grab(grab, serial);
|
||||
}
|
||||
XdgRequest::AckConfigure {
|
||||
surface, configure, ..
|
||||
surface,
|
||||
configure: Configure::Toplevel(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>>() {
|
||||
let waiting_for_serial = with_states(&surface, |states| {
|
||||
if let Some(data) = states.data_map.get::<RefCell<SurfaceData>>() {
|
||||
if let ResizeState::WaitingForFinalAck(_, serial) = data.borrow().resize_state {
|
||||
return Some(serial);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
if let Some(serial) = waiting_for_serial {
|
||||
if configure.serial > serial {
|
||||
|
@ -486,73 +458,64 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
|||
// 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
|
||||
if serial == configure.serial
|
||||
&& configure.state.states.contains(xdg_toplevel::State::Resizing)
|
||||
{
|
||||
with_states(&surface, |states| {
|
||||
let mut data = states
|
||||
.data_map
|
||||
.get::<RefCell<SurfaceData>>()
|
||||
.unwrap()
|
||||
.borrow_mut();
|
||||
if let ResizeState::WaitingForFinalAck(resize_data, _) = data.resize_state
|
||||
{
|
||||
if let ResizeState::WaitingForFinalAck(resize_data, _) = data.resize_state {
|
||||
data.resize_state = ResizeState::WaitingForCommit(resize_data);
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
XdgRequest::Fullscreen { surface, output, .. } => {
|
||||
if surface
|
||||
.with_pending_state(|state| {
|
||||
let ret = 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()
|
||||
{
|
||||
});
|
||||
if ret.is_ok() {
|
||||
surface.send_configure();
|
||||
}
|
||||
}
|
||||
XdgRequest::UnFullscreen { surface } => {
|
||||
if surface
|
||||
.with_pending_state(|state| {
|
||||
let ret = surface.with_pending_state(|state| {
|
||||
state.states.unset(xdg_toplevel::State::Fullscreen);
|
||||
state.size = None;
|
||||
state.fullscreen_output = None;
|
||||
})
|
||||
.is_ok()
|
||||
{
|
||||
});
|
||||
if ret.is_ok() {
|
||||
surface.send_configure();
|
||||
}
|
||||
}
|
||||
XdgRequest::Maximize { surface } => {
|
||||
if surface
|
||||
.with_pending_state(|state| {
|
||||
let ret = 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()
|
||||
{
|
||||
});
|
||||
if ret.is_ok() {
|
||||
surface.send_configure();
|
||||
}
|
||||
}
|
||||
XdgRequest::UnMaximize { surface } => {
|
||||
if surface
|
||||
.with_pending_state(|state| {
|
||||
let ret = surface.with_pending_state(|state| {
|
||||
state.states.unset(xdg_toplevel::State::Maximized);
|
||||
state.size = None;
|
||||
})
|
||||
.is_ok()
|
||||
{
|
||||
});
|
||||
if ret.is_ok() {
|
||||
surface.send_configure();
|
||||
}
|
||||
}
|
||||
|
@ -565,8 +528,7 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
|||
let shell_window_map = window_map.clone();
|
||||
let (wl_shell_state, _) = wl_shell_init(
|
||||
display,
|
||||
compositor_token,
|
||||
move |req: ShellRequest<_>| {
|
||||
move |req: ShellRequest| {
|
||||
match req {
|
||||
ShellRequest::SetKind {
|
||||
surface,
|
||||
|
@ -658,9 +620,9 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
|||
let geometry = shell_window_map.borrow().geometry(&toplevel).unwrap();
|
||||
let initial_window_size = (geometry.width, geometry.height);
|
||||
|
||||
compositor_token.with_surface_data(surface.get_surface().unwrap(), move |attrs| {
|
||||
attrs
|
||||
.user_data
|
||||
with_states(surface.get_surface().unwrap(), move |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<RefCell<SurfaceData>>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
|
@ -669,11 +631,11 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
|||
initial_window_location,
|
||||
initial_window_size,
|
||||
});
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let grab = ResizeSurfaceGrab {
|
||||
start_data,
|
||||
ctoken: compositor_token,
|
||||
toplevel,
|
||||
edges: edges.into(),
|
||||
initial_window_size,
|
||||
|
@ -689,7 +651,6 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
|||
);
|
||||
|
||||
ShellHandles {
|
||||
token: compositor_token,
|
||||
xdg_state: xdg_shell_state,
|
||||
wl_state: wl_shell_state,
|
||||
window_map,
|
||||
|
@ -726,82 +687,43 @@ impl Default for ResizeState {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct CommitedState {
|
||||
pub buffer: Option<wl_buffer::WlBuffer>,
|
||||
pub input_region: Option<RegionAttributes>,
|
||||
pub dimensions: Option<(i32, i32)>,
|
||||
pub frame_callbacks: Vec<wl_callback::WlCallback>,
|
||||
pub sub_location: (i32, i32),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SurfaceData {
|
||||
pub buffer: Option<wl_buffer::WlBuffer>,
|
||||
pub texture: Option<Box<dyn std::any::Any + 'static>>,
|
||||
pub geometry: Option<Rectangle>,
|
||||
pub resize_state: ResizeState,
|
||||
/// Minimum width and height, as requested by the surface.
|
||||
///
|
||||
/// `0` means unlimited.
|
||||
pub min_size: (i32, i32),
|
||||
/// Maximum width and height, as requested by the surface.
|
||||
///
|
||||
/// `0` means unlimited.
|
||||
pub max_size: (i32, i32),
|
||||
pub current_state: CommitedState,
|
||||
pub cached_state: Option<CommitedState>,
|
||||
pub dimensions: Option<(i32, i32)>,
|
||||
}
|
||||
|
||||
impl SurfaceData {
|
||||
/// Apply a next state into the surface current state
|
||||
pub fn apply_state(&mut self, next_state: CommitedState) {
|
||||
if Self::merge_state(&mut self.current_state, next_state) {
|
||||
let _ = self.texture.take();
|
||||
pub fn update_buffer(&mut self, attrs: &mut SurfaceAttributes) {
|
||||
match attrs.buffer.take() {
|
||||
Some(BufferAssignment::NewBuffer { buffer, .. }) => {
|
||||
// new contents
|
||||
self.dimensions = buffer_dimensions(&buffer);
|
||||
if let Some(old_buffer) = std::mem::replace(&mut self.buffer, Some(buffer)) {
|
||||
old_buffer.release();
|
||||
}
|
||||
self.texture = None;
|
||||
}
|
||||
Some(BufferAssignment::Removed) => {
|
||||
// remove the contents
|
||||
self.buffer = None;
|
||||
self.dimensions = None;
|
||||
self.texture = None;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply a next state into the cached state
|
||||
pub fn apply_cache(&mut self, next_state: CommitedState) {
|
||||
match self.cached_state {
|
||||
Some(ref mut cached) => {
|
||||
Self::merge_state(cached, next_state);
|
||||
}
|
||||
None => self.cached_state = Some(next_state),
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply the current cached state if any
|
||||
pub fn apply_from_cache(&mut self) {
|
||||
if let Some(cached) = self.cached_state.take() {
|
||||
self.apply_state(cached);
|
||||
}
|
||||
}
|
||||
|
||||
// merge the "next" state into the "into" state
|
||||
//
|
||||
// returns true if the texture cache should be invalidated
|
||||
fn merge_state(into: &mut CommitedState, next: CommitedState) -> bool {
|
||||
// release the previous buffer if relevant
|
||||
let new_buffer = into.buffer != next.buffer;
|
||||
if new_buffer {
|
||||
if let Some(buffer) = into.buffer.take() {
|
||||
buffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
*into = next;
|
||||
new_buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl SurfaceData {
|
||||
/// Returns the size of the surface.
|
||||
pub fn size(&self) -> Option<(i32, i32)> {
|
||||
self.current_state.dimensions
|
||||
self.dimensions
|
||||
}
|
||||
|
||||
/// Checks if the surface's input region contains the point.
|
||||
pub fn contains_point(&self, point: (f64, f64)) -> bool {
|
||||
pub fn contains_point(&self, attrs: &SurfaceAttributes, point: (f64, f64)) -> bool {
|
||||
let (w, h) = match self.size() {
|
||||
None => return false, // If the surface has no size, it can't have an input region.
|
||||
Some(wh) => wh,
|
||||
|
@ -823,170 +745,73 @@ impl SurfaceData {
|
|||
}
|
||||
|
||||
// If there's no input region, we're done.
|
||||
if self.current_state.input_region.is_none() {
|
||||
if attrs.input_region.is_none() {
|
||||
return true;
|
||||
}
|
||||
|
||||
self.current_state.input_region.as_ref().unwrap().contains(point)
|
||||
attrs.input_region.as_ref().unwrap().contains(point)
|
||||
}
|
||||
|
||||
/// Send the frame callback if it had been requested
|
||||
pub fn send_frame(&mut self, time: u32) {
|
||||
for callback in self.current_state.frame_callbacks.drain(..) {
|
||||
pub fn send_frame(attrs: &mut SurfaceAttributes, time: u32) {
|
||||
for callback in attrs.frame_callbacks.drain(..) {
|
||||
callback.done(time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn surface_commit(
|
||||
surface: &wl_surface::WlSurface,
|
||||
token: CompositorToken<Roles>,
|
||||
window_map: &RefCell<MyWindowMap>,
|
||||
) {
|
||||
fn surface_commit(surface: &wl_surface::WlSurface, window_map: &RefCell<WindowMap>) {
|
||||
#[cfg(feature = "xwayland")]
|
||||
super::xwayland::commit_hook(surface);
|
||||
|
||||
let mut geometry = None;
|
||||
let mut min_size = (0, 0);
|
||||
let mut max_size = (0, 0);
|
||||
let mut window_map = window_map.borrow_mut();
|
||||
|
||||
if token.has_role::<XdgToplevelSurfaceRole>(surface) {
|
||||
if let Some(SurfaceKind::Xdg(xdg)) = window_map.borrow().find(surface) {
|
||||
xdg.commit();
|
||||
}
|
||||
|
||||
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)
|
||||
.ok();
|
||||
|
||||
let (refresh, apply_children) = token.with_surface_data(surface, |attributes| {
|
||||
attributes
|
||||
.user_data
|
||||
if !is_sync_subsurface(surface) {
|
||||
// Update the buffer of all child surfaces
|
||||
with_surface_tree_upward(
|
||||
surface,
|
||||
(),
|
||||
|_, _, _| TraversalAction::DoChildren(()),
|
||||
|_, states, _| {
|
||||
states
|
||||
.data_map
|
||||
.insert_if_missing(|| RefCell::new(SurfaceData::default()));
|
||||
let mut data = attributes
|
||||
.user_data
|
||||
let mut data = states
|
||||
.data_map
|
||||
.get::<RefCell<SurfaceData>>()
|
||||
.unwrap()
|
||||
.borrow_mut();
|
||||
|
||||
let mut next_state = match data.cached_state {
|
||||
// There is a pending state, accumulate into it
|
||||
Some(ref cached_state) => cached_state.clone(),
|
||||
// Start from the current state
|
||||
None => data.current_state.clone(),
|
||||
};
|
||||
|
||||
if let Some(ref data) = sub_data {
|
||||
next_state.sub_location = data.location;
|
||||
}
|
||||
|
||||
data.geometry = geometry;
|
||||
next_state.input_region = attributes.input_region.clone();
|
||||
data.min_size = min_size;
|
||||
data.max_size = max_size;
|
||||
|
||||
// we retrieve the contents of the associated buffer and copy it
|
||||
match attributes.buffer.take() {
|
||||
Some(BufferAssignment::NewBuffer { buffer, .. }) => {
|
||||
// new contents
|
||||
next_state.dimensions = buffer_dimensions(&buffer);
|
||||
next_state.buffer = Some(buffer);
|
||||
}
|
||||
Some(BufferAssignment::Removed) => {
|
||||
// remove the contents
|
||||
next_state.buffer = None;
|
||||
next_state.dimensions = None;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
// Append the current frame callbacks to the next state
|
||||
next_state
|
||||
.frame_callbacks
|
||||
.extend(attributes.frame_callbacks.drain(..));
|
||||
|
||||
data.apply_cache(next_state);
|
||||
|
||||
let apply_children = if let Some(SubsurfaceRole { sync: true, .. }) = sub_data {
|
||||
false
|
||||
} else {
|
||||
data.apply_from_cache();
|
||||
true
|
||||
};
|
||||
|
||||
(window_map.borrow().find(surface), apply_children)
|
||||
});
|
||||
|
||||
// Apply the cached state of all sync children
|
||||
if apply_children {
|
||||
token.with_surface_tree_upward(
|
||||
surface,
|
||||
true,
|
||||
|_, _, role, &is_root| {
|
||||
// only process children if the surface is sync or we are the root
|
||||
if is_root {
|
||||
// we are the root
|
||||
TraversalAction::DoChildren(false)
|
||||
} else if let Ok(sub_data) = Role::<SubsurfaceRole>::data(role) {
|
||||
if sub_data.sync || is_root {
|
||||
TraversalAction::DoChildren(false)
|
||||
} else {
|
||||
// if we are not sync, we won't apply from cache and don't process
|
||||
// the children
|
||||
TraversalAction::SkipChildren
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
data.update_buffer(&mut *states.cached_state.current::<SurfaceAttributes>());
|
||||
},
|
||||
|_, attributes, role, _| {
|
||||
// only apply from cache if we are a sync subsurface
|
||||
if let Ok(sub_data) = Role::<SubsurfaceRole>::data(role) {
|
||||
if sub_data.sync {
|
||||
if let Some(data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
|
||||
data.borrow_mut().apply_from_cache();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|_, _, _, _| true,
|
||||
)
|
||||
|_, _, _| true,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(toplevel) = window_map.find(surface) {
|
||||
// send the initial configure if relevant
|
||||
if let SurfaceKind::Xdg(ref toplevel) = toplevel {
|
||||
let initial_configure_sent = with_states(surface, |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.initial_configure_sent
|
||||
})
|
||||
.unwrap();
|
||||
if !initial_configure_sent {
|
||||
toplevel.send_configure();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(toplevel) = refresh {
|
||||
let mut window_map = window_map.borrow_mut();
|
||||
window_map.refresh_toplevel(&toplevel);
|
||||
// Get the geometry outside since it uses the token, and so would block inside.
|
||||
let Rectangle { width, height, .. } = window_map.geometry(&toplevel).unwrap();
|
||||
|
||||
let new_location = token.with_surface_data(surface, |attributes| {
|
||||
let mut data = attributes
|
||||
.user_data
|
||||
let new_location = with_states(surface, |states| {
|
||||
let mut data = states
|
||||
.data_map
|
||||
.get::<RefCell<SurfaceData>>()
|
||||
.unwrap()
|
||||
.borrow_mut();
|
||||
|
@ -1027,7 +852,8 @@ fn surface_commit(
|
|||
}
|
||||
|
||||
new_location
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
if let Some(location) = new_location {
|
||||
window_map.set_location(&toplevel, location);
|
||||
|
|
|
@ -13,7 +13,6 @@ use smithay::{
|
|||
wayland_server::{protocol::wl_surface::WlSurface, Display},
|
||||
},
|
||||
wayland::{
|
||||
compositor::CompositorToken,
|
||||
data_device::{default_action_chooser, init_data_device, set_data_device_focus, DataDeviceEvent},
|
||||
seat::{CursorImageStatus, KeyboardHandle, PointerHandle, Seat, XkbConfig},
|
||||
shm::init_shm_global,
|
||||
|
@ -31,8 +30,7 @@ pub struct AnvilState<BackendData> {
|
|||
pub running: Arc<AtomicBool>,
|
||||
pub display: Rc<RefCell<Display>>,
|
||||
pub handle: LoopHandle<'static, AnvilState<BackendData>>,
|
||||
pub ctoken: CompositorToken<crate::shell::Roles>,
|
||||
pub window_map: Rc<RefCell<crate::window_map::WindowMap<crate::shell::Roles>>>,
|
||||
pub window_map: Rc<RefCell<crate::window_map::WindowMap>>,
|
||||
pub dnd_icon: Arc<Mutex<Option<WlSurface>>>,
|
||||
pub log: slog::Logger,
|
||||
// input-related fields
|
||||
|
@ -106,24 +104,18 @@ impl<BackendData: Backend + 'static> AnvilState<BackendData> {
|
|||
_ => {}
|
||||
},
|
||||
default_action_chooser,
|
||||
shell_handles.token,
|
||||
log.clone(),
|
||||
);
|
||||
|
||||
// init input
|
||||
let seat_name = backend_data.seat_name();
|
||||
|
||||
let (mut seat, _) = Seat::new(
|
||||
&mut display.borrow_mut(),
|
||||
seat_name.clone(),
|
||||
shell_handles.token,
|
||||
log.clone(),
|
||||
);
|
||||
let (mut seat, _) = Seat::new(&mut display.borrow_mut(), seat_name.clone(), log.clone());
|
||||
|
||||
let cursor_status = Arc::new(Mutex::new(CursorImageStatus::Default));
|
||||
|
||||
let cursor_status2 = cursor_status.clone();
|
||||
let pointer = seat.add_pointer(shell_handles.token, move |new_status| {
|
||||
let pointer = seat.add_pointer(move |new_status| {
|
||||
// TODO: hide winit system cursor when relevant
|
||||
*cursor_status2.lock().unwrap() = new_status
|
||||
});
|
||||
|
@ -155,7 +147,6 @@ impl<BackendData: Backend + 'static> AnvilState<BackendData> {
|
|||
running: Arc::new(AtomicBool::new(true)),
|
||||
display,
|
||||
handle,
|
||||
ctoken: shell_handles.token,
|
||||
window_map: shell_handles.window_map,
|
||||
dnd_icon,
|
||||
log,
|
||||
|
|
|
@ -51,7 +51,6 @@ use smithay::{
|
|||
signaling::{Linkable, SignalToken, Signaler},
|
||||
utils::Rectangle,
|
||||
wayland::{
|
||||
compositor::CompositorToken,
|
||||
output::{Mode, Output, PhysicalProperties},
|
||||
seat::CursorImageStatus,
|
||||
},
|
||||
|
@ -66,9 +65,8 @@ use smithay::{
|
|||
wayland::dmabuf::init_dmabuf_global,
|
||||
};
|
||||
|
||||
use crate::drawing::*;
|
||||
use crate::shell::{MyWindowMap, Roles};
|
||||
use crate::state::{AnvilState, Backend};
|
||||
use crate::{drawing::*, window_map::WindowMap};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SessionFd(RawFd);
|
||||
|
@ -632,7 +630,6 @@ impl AnvilState<UdevData> {
|
|||
crtc,
|
||||
&mut *self.window_map.borrow_mut(),
|
||||
&mut self.backend_data.output_map,
|
||||
&self.ctoken,
|
||||
&self.pointer_location,
|
||||
&device_backend.pointer_image,
|
||||
&*self.dnd_icon.lock().unwrap(),
|
||||
|
@ -678,9 +675,8 @@ fn render_surface(
|
|||
renderer: &mut Gles2Renderer,
|
||||
device_id: dev_t,
|
||||
crtc: crtc::Handle,
|
||||
window_map: &mut MyWindowMap,
|
||||
window_map: &mut WindowMap,
|
||||
output_map: &mut Vec<MyOutput>,
|
||||
compositor_token: &CompositorToken<Roles>,
|
||||
pointer_location: &(f64, f64),
|
||||
pointer_image: &Gles2Texture,
|
||||
dnd_icon: &Option<wl_surface::WlSurface>,
|
||||
|
@ -721,7 +717,6 @@ fn render_surface(
|
|||
width: width as i32,
|
||||
height: height as i32,
|
||||
}),
|
||||
*compositor_token,
|
||||
logger,
|
||||
)?;
|
||||
|
||||
|
@ -736,14 +731,7 @@ fn render_surface(
|
|||
{
|
||||
if let Some(ref wl_surface) = dnd_icon.as_ref() {
|
||||
if wl_surface.as_ref().is_alive() {
|
||||
draw_dnd_icon(
|
||||
renderer,
|
||||
frame,
|
||||
wl_surface,
|
||||
(ptr_x, ptr_y),
|
||||
*compositor_token,
|
||||
logger,
|
||||
)?;
|
||||
draw_dnd_icon(renderer, frame, wl_surface, (ptr_x, ptr_y), logger)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -759,20 +747,12 @@ fn render_surface(
|
|||
}
|
||||
|
||||
if let CursorImageStatus::Image(ref wl_surface) = *cursor_status {
|
||||
draw_cursor(
|
||||
renderer,
|
||||
frame,
|
||||
wl_surface,
|
||||
(ptr_x, ptr_y),
|
||||
*compositor_token,
|
||||
logger,
|
||||
)?;
|
||||
draw_cursor(renderer, frame, wl_surface, (ptr_x, ptr_y), logger)?;
|
||||
} else {
|
||||
frame.render_texture_at(pointer_image, (ptr_x, ptr_y), Transform::Normal, 1.0)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
|
|
|
@ -4,10 +4,10 @@ use smithay::{
|
|||
reexports::wayland_server::protocol::wl_surface,
|
||||
utils::Rectangle,
|
||||
wayland::{
|
||||
compositor::{roles::Role, CompositorToken, SubsurfaceRole, TraversalAction},
|
||||
compositor::{with_states, with_surface_tree_downward, SubsurfaceCachedState, TraversalAction},
|
||||
shell::{
|
||||
legacy::{ShellSurface, ShellSurfaceRole},
|
||||
xdg::{PopupSurface, ToplevelSurface, XdgPopupSurfaceRole, XdgToplevelSurfaceRole},
|
||||
legacy::ShellSurface,
|
||||
xdg::{PopupSurface, SurfaceCachedState, ToplevelSurface},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -16,29 +16,15 @@ use crate::shell::SurfaceData;
|
|||
#[cfg(feature = "xwayland")]
|
||||
use crate::xwayland::X11Surface;
|
||||
|
||||
pub enum Kind<R> {
|
||||
Xdg(ToplevelSurface<R>),
|
||||
Wl(ShellSurface<R>),
|
||||
#[derive(Clone)]
|
||||
pub enum Kind {
|
||||
Xdg(ToplevelSurface),
|
||||
Wl(ShellSurface),
|
||||
#[cfg(feature = "xwayland")]
|
||||
X11(X11Surface),
|
||||
}
|
||||
|
||||
// We implement Clone manually because #[derive(..)] would require R: Clone.
|
||||
impl<R> Clone for Kind<R> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Kind::Xdg(xdg) => Kind::Xdg(xdg.clone()),
|
||||
Kind::Wl(wl) => Kind::Wl(wl.clone()),
|
||||
#[cfg(feature = "xwayland")]
|
||||
Kind::X11(x11) => Kind::X11(x11.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> Kind<R>
|
||||
where
|
||||
R: Role<SubsurfaceRole> + Role<XdgToplevelSurfaceRole> + Role<ShellSurfaceRole> + 'static,
|
||||
{
|
||||
impl Kind {
|
||||
pub fn alive(&self) -> bool {
|
||||
match *self {
|
||||
Kind::Xdg(ref t) => t.alive(),
|
||||
|
@ -68,23 +54,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub enum PopupKind<R> {
|
||||
Xdg(PopupSurface<R>),
|
||||
#[derive(Clone)]
|
||||
pub enum PopupKind {
|
||||
Xdg(PopupSurface),
|
||||
}
|
||||
|
||||
// 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,
|
||||
{
|
||||
impl PopupKind {
|
||||
pub fn alive(&self) -> bool {
|
||||
match *self {
|
||||
PopupKind::Xdg(ref t) => t.alive(),
|
||||
|
@ -97,56 +72,53 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
struct Window<R> {
|
||||
struct Window {
|
||||
location: (i32, i32),
|
||||
/// A bounding box over this window and its children.
|
||||
///
|
||||
/// Used for the fast path of the check in `matching`, and as the fall-back for the window
|
||||
/// geometry if that's not set explicitly.
|
||||
bbox: Rectangle,
|
||||
toplevel: Kind<R>,
|
||||
toplevel: Kind,
|
||||
}
|
||||
|
||||
impl<R> Window<R>
|
||||
where
|
||||
R: Role<SubsurfaceRole> + Role<XdgToplevelSurfaceRole> + Role<ShellSurfaceRole> + 'static,
|
||||
{
|
||||
impl Window {
|
||||
/// Finds the topmost surface under this point if any and returns it together with the location of this
|
||||
/// surface.
|
||||
fn matching(
|
||||
&self,
|
||||
point: (f64, f64),
|
||||
ctoken: CompositorToken<R>,
|
||||
) -> Option<(wl_surface::WlSurface, (f64, f64))> {
|
||||
fn matching(&self, point: (f64, f64)) -> Option<(wl_surface::WlSurface, (f64, f64))> {
|
||||
if !self.bbox.contains((point.0 as i32, point.1 as i32)) {
|
||||
return None;
|
||||
}
|
||||
// need to check more carefully
|
||||
let found = RefCell::new(None);
|
||||
if let Some(wl_surface) = self.toplevel.get_surface() {
|
||||
ctoken.with_surface_tree_downward(
|
||||
with_surface_tree_downward(
|
||||
wl_surface,
|
||||
self.location,
|
||||
|wl_surface, attributes, role, &(mut x, mut y)| {
|
||||
let data = attributes.user_data.get::<RefCell<SurfaceData>>();
|
||||
|wl_surface, states, &(mut x, mut y)| {
|
||||
let data = states.data_map.get::<RefCell<SurfaceData>>();
|
||||
|
||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||
x += subdata.location.0;
|
||||
y += subdata.location.1;
|
||||
if states.role == Some("subsurface") {
|
||||
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
||||
x += current.location.0;
|
||||
y += current.location.1;
|
||||
}
|
||||
|
||||
let surface_local_point = (point.0 - x as f64, point.1 - y as f64);
|
||||
if data
|
||||
.map(|data| data.borrow().contains_point(surface_local_point))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
let contains_the_point = data
|
||||
.map(|data| {
|
||||
data.borrow()
|
||||
.contains_point(&*states.cached_state.current(), surface_local_point)
|
||||
})
|
||||
.unwrap_or(false);
|
||||
if contains_the_point {
|
||||
*found.borrow_mut() = Some((wl_surface.clone(), (x as f64, y as f64)));
|
||||
}
|
||||
|
||||
TraversalAction::DoChildren((x, y))
|
||||
},
|
||||
|_, _, _, _| {},
|
||||
|_, _, _, _| {
|
||||
|_, _, _| {},
|
||||
|_, _, _| {
|
||||
// only continue if the point is not found
|
||||
found.borrow().is_none()
|
||||
},
|
||||
|
@ -155,20 +127,21 @@ where
|
|||
found.into_inner()
|
||||
}
|
||||
|
||||
fn self_update(&mut self, ctoken: CompositorToken<R>) {
|
||||
fn self_update(&mut self) {
|
||||
let (base_x, base_y) = self.location;
|
||||
let (mut min_x, mut min_y, mut max_x, mut max_y) = (base_x, base_y, base_x, base_y);
|
||||
if let Some(wl_surface) = self.toplevel.get_surface() {
|
||||
ctoken.with_surface_tree_downward(
|
||||
with_surface_tree_downward(
|
||||
wl_surface,
|
||||
(base_x, base_y),
|
||||
|_, attributes, role, &(mut x, mut y)| {
|
||||
let data = attributes.user_data.get::<RefCell<SurfaceData>>();
|
||||
|_, states, &(mut x, mut y)| {
|
||||
let data = states.data_map.get::<RefCell<SurfaceData>>();
|
||||
|
||||
if let Some((w, h)) = data.and_then(|d| d.borrow().size()) {
|
||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||
x += subdata.location.0;
|
||||
y += subdata.location.1;
|
||||
if states.role == Some("subsurface") {
|
||||
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
||||
x += current.location.0;
|
||||
y += current.location.1;
|
||||
}
|
||||
|
||||
// Update the bounding box.
|
||||
|
@ -184,8 +157,8 @@ where
|
|||
TraversalAction::SkipChildren
|
||||
}
|
||||
},
|
||||
|_, _, _, _| {},
|
||||
|_, _, _, _| true,
|
||||
|_, _, _| {},
|
||||
|_, _, _| true,
|
||||
);
|
||||
}
|
||||
self.bbox = Rectangle {
|
||||
|
@ -197,85 +170,69 @@ where
|
|||
}
|
||||
|
||||
/// Returns the geometry of this window.
|
||||
pub fn geometry(&self, ctoken: CompositorToken<R>) -> Rectangle {
|
||||
pub fn geometry(&self) -> Rectangle {
|
||||
// It's the set geometry with the full bounding box as the fallback.
|
||||
ctoken
|
||||
.with_surface_data(self.toplevel.get_surface().unwrap(), |attributes| {
|
||||
attributes
|
||||
.user_data
|
||||
.get::<RefCell<SurfaceData>>()
|
||||
.unwrap()
|
||||
.borrow()
|
||||
.geometry
|
||||
with_states(self.toplevel.get_surface().unwrap(), |states| {
|
||||
states.cached_state.current::<SurfaceCachedState>().geometry
|
||||
})
|
||||
.unwrap()
|
||||
.unwrap_or(self.bbox)
|
||||
}
|
||||
|
||||
/// Sends the frame callback to all the subsurfaces in this
|
||||
/// window that requested it
|
||||
pub fn send_frame(&self, time: u32, ctoken: CompositorToken<R>) {
|
||||
pub fn send_frame(&self, time: u32) {
|
||||
if let Some(wl_surface) = self.toplevel.get_surface() {
|
||||
ctoken.with_surface_tree_downward(
|
||||
with_surface_tree_downward(
|
||||
wl_surface,
|
||||
(),
|
||||
|_, _, _, &()| TraversalAction::DoChildren(()),
|
||||
|_, attributes, _, &()| {
|
||||
|_, _, &()| TraversalAction::DoChildren(()),
|
||||
|_, states, &()| {
|
||||
// the surface may not have any user_data if it is a subsurface and has not
|
||||
// yet been commited
|
||||
if let Some(data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
|
||||
data.borrow_mut().send_frame(time)
|
||||
}
|
||||
SurfaceData::send_frame(&mut *states.cached_state.current(), time)
|
||||
},
|
||||
|_, _, _, &()| true,
|
||||
|_, _, &()| true,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Popup<R> {
|
||||
popup: PopupKind<R>,
|
||||
pub struct Popup {
|
||||
popup: PopupKind,
|
||||
}
|
||||
|
||||
pub struct WindowMap<R> {
|
||||
ctoken: CompositorToken<R>,
|
||||
windows: Vec<Window<R>>,
|
||||
popups: Vec<Popup<R>>,
|
||||
pub struct WindowMap {
|
||||
windows: Vec<Window>,
|
||||
popups: Vec<Popup>,
|
||||
}
|
||||
|
||||
impl<R> WindowMap<R>
|
||||
where
|
||||
R: Role<SubsurfaceRole>
|
||||
+ Role<XdgToplevelSurfaceRole>
|
||||
+ Role<XdgPopupSurfaceRole>
|
||||
+ Role<ShellSurfaceRole>
|
||||
+ 'static,
|
||||
{
|
||||
pub fn new(ctoken: CompositorToken<R>) -> Self {
|
||||
impl WindowMap {
|
||||
pub fn new() -> Self {
|
||||
WindowMap {
|
||||
ctoken,
|
||||
windows: Vec::new(),
|
||||
popups: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, toplevel: Kind<R>, location: (i32, i32)) {
|
||||
pub fn insert(&mut self, toplevel: Kind, location: (i32, i32)) {
|
||||
let mut window = Window {
|
||||
location,
|
||||
bbox: Rectangle::default(),
|
||||
toplevel,
|
||||
};
|
||||
window.self_update(self.ctoken);
|
||||
window.self_update();
|
||||
self.windows.insert(0, window);
|
||||
}
|
||||
|
||||
pub fn insert_popup(&mut self, popup: PopupKind<R>) {
|
||||
pub fn insert_popup(&mut self, popup: PopupKind) {
|
||||
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) {
|
||||
if let Some(surface) = w.matching(point) {
|
||||
return Some(surface);
|
||||
}
|
||||
}
|
||||
|
@ -288,7 +245,7 @@ where
|
|||
) -> Option<(wl_surface::WlSurface, (f64, f64))> {
|
||||
let mut found = None;
|
||||
for (i, w) in self.windows.iter().enumerate() {
|
||||
if let Some(surface) = w.matching(point, self.ctoken) {
|
||||
if let Some(surface) = w.matching(point) {
|
||||
found = Some((i, surface));
|
||||
break;
|
||||
}
|
||||
|
@ -304,7 +261,7 @@ where
|
|||
|
||||
pub fn with_windows_from_bottom_to_top<Func>(&self, mut f: Func)
|
||||
where
|
||||
Func: FnMut(&Kind<R>, (i32, i32), &Rectangle),
|
||||
Func: FnMut(&Kind, (i32, i32), &Rectangle),
|
||||
{
|
||||
for w in self.windows.iter().rev() {
|
||||
f(&w.toplevel, w.location, &w.bbox)
|
||||
|
@ -315,14 +272,14 @@ where
|
|||
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);
|
||||
w.self_update();
|
||||
}
|
||||
}
|
||||
|
||||
/// Refreshes the state of the toplevel, if it exists.
|
||||
pub fn refresh_toplevel(&mut self, toplevel: &Kind<R>) {
|
||||
pub fn refresh_toplevel(&mut self, toplevel: &Kind) {
|
||||
if let Some(w) = self.windows.iter_mut().find(|w| w.toplevel.equals(toplevel)) {
|
||||
w.self_update(self.ctoken);
|
||||
w.self_update();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -331,7 +288,7 @@ where
|
|||
}
|
||||
|
||||
/// Finds the toplevel corresponding to the given `WlSurface`.
|
||||
pub fn find(&self, surface: &wl_surface::WlSurface) -> Option<Kind<R>> {
|
||||
pub fn find(&self, surface: &wl_surface::WlSurface) -> Option<Kind> {
|
||||
self.windows.iter().find_map(|w| {
|
||||
if w.toplevel
|
||||
.get_surface()
|
||||
|
@ -345,7 +302,7 @@ where
|
|||
})
|
||||
}
|
||||
|
||||
pub fn find_popup(&self, surface: &wl_surface::WlSurface) -> Option<PopupKind<R>> {
|
||||
pub fn find_popup(&self, surface: &wl_surface::WlSurface) -> Option<PopupKind> {
|
||||
self.popups.iter().find_map(|p| {
|
||||
if p.popup
|
||||
.get_surface()
|
||||
|
@ -360,7 +317,7 @@ where
|
|||
}
|
||||
|
||||
/// 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) -> Option<(i32, i32)> {
|
||||
self.windows
|
||||
.iter()
|
||||
.find(|w| w.toplevel.equals(toplevel))
|
||||
|
@ -368,24 +325,24 @@ where
|
|||
}
|
||||
|
||||
/// Sets the location of the toplevel, if it exists.
|
||||
pub fn set_location(&mut self, toplevel: &Kind<R>, location: (i32, i32)) {
|
||||
pub fn set_location(&mut self, toplevel: &Kind, location: (i32, i32)) {
|
||||
if let Some(w) = self.windows.iter_mut().find(|w| w.toplevel.equals(toplevel)) {
|
||||
w.location = location;
|
||||
w.self_update(self.ctoken);
|
||||
w.self_update();
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the geometry of the toplevel, if it exists.
|
||||
pub fn geometry(&self, toplevel: &Kind<R>) -> Option<Rectangle> {
|
||||
pub fn geometry(&self, toplevel: &Kind) -> Option<Rectangle> {
|
||||
self.windows
|
||||
.iter()
|
||||
.find(|w| w.toplevel.equals(toplevel))
|
||||
.map(|w| w.geometry(self.ctoken))
|
||||
.map(|w| w.geometry())
|
||||
}
|
||||
|
||||
pub fn send_frames(&self, time: u32) {
|
||||
for window in &self.windows {
|
||||
window.send_frame(time, self.ctoken);
|
||||
window.send_frame(time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,12 +61,7 @@ pub fn run_winit(
|
|||
* Initialize the globals
|
||||
*/
|
||||
|
||||
let mut state = AnvilState::init(
|
||||
display.clone(),
|
||||
event_loop.handle(),
|
||||
WinitData,
|
||||
log.clone(),
|
||||
);
|
||||
let mut state = AnvilState::init(display.clone(), event_loop.handle(), WinitData, log.clone());
|
||||
|
||||
let (output, _) = Output::new(
|
||||
&mut display.borrow_mut(),
|
||||
|
@ -122,14 +117,7 @@ pub fn run_winit(
|
|||
frame.clear([0.8, 0.8, 0.9, 1.0])?;
|
||||
|
||||
// draw the windows
|
||||
draw_windows(
|
||||
renderer,
|
||||
frame,
|
||||
&*state.window_map.borrow(),
|
||||
None,
|
||||
state.ctoken,
|
||||
&log,
|
||||
)?;
|
||||
draw_windows(renderer, frame, &*state.window_map.borrow(), None, &log)?;
|
||||
|
||||
let (x, y) = state.pointer_location;
|
||||
// draw the dnd icon if any
|
||||
|
@ -137,14 +125,7 @@ pub fn run_winit(
|
|||
let guard = state.dnd_icon.lock().unwrap();
|
||||
if let Some(ref surface) = *guard {
|
||||
if surface.as_ref().is_alive() {
|
||||
draw_dnd_icon(
|
||||
renderer,
|
||||
frame,
|
||||
surface,
|
||||
(x as i32, y as i32),
|
||||
state.ctoken,
|
||||
&log,
|
||||
)?;
|
||||
draw_dnd_icon(renderer, frame, surface, (x as i32, y as i32), &log)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -163,7 +144,7 @@ pub fn run_winit(
|
|||
// draw as relevant
|
||||
if let CursorImageStatus::Image(ref surface) = *guard {
|
||||
cursor_visible = false;
|
||||
draw_cursor(renderer, frame, surface, (x as i32, y as i32), state.ctoken, &log)?;
|
||||
draw_cursor(renderer, frame, surface, (x as i32, y as i32), &log)?;
|
||||
} else {
|
||||
cursor_visible = true;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::{cell::RefCell, collections::HashMap, convert::TryFrom, os::unix::net::
|
|||
|
||||
use smithay::{
|
||||
reexports::wayland_server::{protocol::wl_surface::WlSurface, Client},
|
||||
wayland::compositor::CompositorToken,
|
||||
wayland::compositor::give_role,
|
||||
};
|
||||
|
||||
use x11rb::{
|
||||
|
@ -20,8 +20,7 @@ use x11rb::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
shell::{MyWindowMap, Roles},
|
||||
window_map::Kind,
|
||||
window_map::{Kind, WindowMap},
|
||||
AnvilState,
|
||||
};
|
||||
|
||||
|
@ -37,8 +36,7 @@ impl<BackendData: 'static> AnvilState<BackendData> {
|
|||
}
|
||||
|
||||
pub fn xwayland_ready(&mut self, connection: UnixStream, client: Client) {
|
||||
let (wm, source) =
|
||||
X11State::start_wm(connection, self.ctoken, self.window_map.clone(), self.log.clone()).unwrap();
|
||||
let (wm, source) = X11State::start_wm(connection, self.window_map.clone(), self.log.clone()).unwrap();
|
||||
let wm = Rc::new(RefCell::new(wm));
|
||||
client.data_map().insert_if_missing(|| Rc::clone(&wm));
|
||||
self.handle
|
||||
|
@ -70,15 +68,13 @@ struct X11State {
|
|||
atoms: Atoms,
|
||||
log: slog::Logger,
|
||||
unpaired_surfaces: HashMap<u32, (Window, (i32, i32))>,
|
||||
token: CompositorToken<Roles>,
|
||||
window_map: Rc<RefCell<MyWindowMap>>,
|
||||
window_map: Rc<RefCell<WindowMap>>,
|
||||
}
|
||||
|
||||
impl X11State {
|
||||
fn start_wm(
|
||||
connection: UnixStream,
|
||||
token: CompositorToken<Roles>,
|
||||
window_map: Rc<RefCell<MyWindowMap>>,
|
||||
window_map: Rc<RefCell<WindowMap>>,
|
||||
log: slog::Logger,
|
||||
) -> Result<(Self, X11Source), Box<dyn std::error::Error>> {
|
||||
// Create an X11 connection. XWayland only uses screen 0.
|
||||
|
@ -123,7 +119,6 @@ impl X11State {
|
|||
conn: Rc::clone(&conn),
|
||||
atoms,
|
||||
unpaired_surfaces: Default::default(),
|
||||
token,
|
||||
window_map,
|
||||
log,
|
||||
};
|
||||
|
@ -209,7 +204,7 @@ impl X11State {
|
|||
fn new_window(&mut self, window: Window, surface: WlSurface, location: (i32, i32)) {
|
||||
debug!(self.log, "Matched X11 surface {:x?} to {:x?}", window, surface);
|
||||
|
||||
if self.token.give_role_with(&surface, X11SurfaceRole).is_err() {
|
||||
if give_role(&surface, "x11_surface").is_err() {
|
||||
// It makes no sense to post a protocol error here since that would only kill Xwayland
|
||||
error!(self.log, "Surface {:x?} already has a role?!", surface);
|
||||
return;
|
||||
|
@ -237,8 +232,6 @@ pub fn commit_hook(surface: &WlSurface) {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct X11SurfaceRole;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct X11Surface {
|
||||
surface: WlSurface,
|
||||
|
|
|
@ -39,8 +39,7 @@ impl<A: AsRawFd + 'static> Allocator<DumbBuffer<A>> for DrmDevice<A> {
|
|||
// dumb buffers are always linear
|
||||
if modifiers
|
||||
.iter()
|
||||
.find(|x| **x == Modifier::Invalid || **x == Modifier::Linear)
|
||||
.is_none()
|
||||
.all(|&x| x != Modifier::Invalid && x != Modifier::Linear)
|
||||
{
|
||||
return Err(drm::SystemError::InvalidArgument);
|
||||
}
|
||||
|
|
|
@ -372,10 +372,9 @@ impl<A: AsRawFd + 'static> LegacyDrmSurface<A> {
|
|||
let encoders = info
|
||||
.encoders()
|
||||
.iter()
|
||||
.filter(|enc| enc.is_some())
|
||||
.map(|enc| enc.unwrap())
|
||||
.flatten()
|
||||
.map(|encoder| {
|
||||
self.fd.get_encoder(encoder).map_err(|source| Error::Access {
|
||||
self.fd.get_encoder(*encoder).map_err(|source| Error::Access {
|
||||
errmsg: "Error loading encoder info",
|
||||
dev: self.fd.dev_path(),
|
||||
source,
|
||||
|
|
|
@ -117,9 +117,11 @@ impl<'a> Debug for EGLPlatform<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Trait describing platform specific functionality to create a valid `EGLDisplay` using the `EGL_EXT_platform_base`(https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) extension.
|
||||
/// Trait describing platform specific functionality to create a valid `EGLDisplay` using the
|
||||
/// [`EGL_EXT_platform_base`](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) extension.
|
||||
pub trait EGLNativeDisplay: Send {
|
||||
/// List of supported platforms that can be used to create a display using [`eglGetPlatformDisplayEXT`](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt)
|
||||
/// List of supported platforms that can be used to create a display using
|
||||
/// [`eglGetPlatformDisplayEXT`](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt)
|
||||
fn supported_platforms(&self) -> Vec<EGLPlatform<'_>>;
|
||||
|
||||
/// Type of surfaces created
|
||||
|
|
|
@ -35,7 +35,7 @@ use super::{ImportDma, ImportShm};
|
|||
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
|
||||
use crate::backend::egl::{display::EGLBufferReader, Format as EGLFormat};
|
||||
#[cfg(feature = "wayland_frontend")]
|
||||
use crate::{utils::Rectangle, wayland::compositor::SurfaceAttributes};
|
||||
use crate::utils::Rectangle;
|
||||
#[cfg(feature = "wayland_frontend")]
|
||||
use wayland_server::protocol::{wl_buffer, wl_shm};
|
||||
|
||||
|
@ -501,7 +501,7 @@ impl ImportShm for Gles2Renderer {
|
|||
fn import_shm_buffer(
|
||||
&mut self,
|
||||
buffer: &wl_buffer::WlBuffer,
|
||||
surface: Option<&SurfaceAttributes>,
|
||||
surface: Option<&crate::wayland::compositor::SurfaceData>,
|
||||
damage: &[Rectangle],
|
||||
) -> Result<Gles2Texture, Gles2Error> {
|
||||
use crate::wayland::shm::with_buffer_contents;
|
||||
|
@ -535,7 +535,7 @@ impl ImportShm for Gles2Renderer {
|
|||
// why not store a `Gles2Texture`? because the user might do so.
|
||||
// this is guaranteed a non-public internal type, so we are good.
|
||||
surface
|
||||
.and_then(|surface| surface.user_data.get::<Rc<Gles2TextureInternal>>().cloned())
|
||||
.and_then(|surface| surface.data_map.get::<Rc<Gles2TextureInternal>>().cloned())
|
||||
.unwrap_or_else(|| {
|
||||
let mut tex = 0;
|
||||
unsafe { self.gl.GenTextures(1, &mut tex) };
|
||||
|
@ -742,9 +742,7 @@ impl Gles2Renderer {
|
|||
texture.0.texture,
|
||||
buffer
|
||||
);
|
||||
if texture.0.is_external {
|
||||
Ok(Some(texture))
|
||||
} else {
|
||||
if !texture.0.is_external {
|
||||
if let Some(egl_images) = texture.0.egl_images.as_ref() {
|
||||
if egl_images[0] == ffi_egl::NO_IMAGE_KHR {
|
||||
return Ok(None);
|
||||
|
@ -753,8 +751,8 @@ impl Gles2Renderer {
|
|||
let tex = Some(texture.0.texture);
|
||||
self.import_egl_image(egl_images[0], false, tex)?;
|
||||
}
|
||||
Ok(Some(texture))
|
||||
}
|
||||
Ok(Some(texture))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use std::collections::HashSet;
|
|||
use std::error::Error;
|
||||
|
||||
#[cfg(feature = "wayland_frontend")]
|
||||
use crate::{utils::Rectangle, wayland::compositor::SurfaceAttributes};
|
||||
use crate::{utils::Rectangle, wayland::compositor::SurfaceData};
|
||||
use cgmath::{prelude::*, Matrix3, Vector2};
|
||||
#[cfg(feature = "wayland_frontend")]
|
||||
use wayland_server::protocol::{wl_buffer, wl_shm};
|
||||
|
@ -264,7 +264,7 @@ pub trait ImportShm: Renderer {
|
|||
fn import_shm_buffer(
|
||||
&mut self,
|
||||
buffer: &wl_buffer::WlBuffer,
|
||||
surface: Option<&SurfaceAttributes>,
|
||||
surface: Option<&crate::wayland::compositor::SurfaceData>,
|
||||
damage: &[Rectangle],
|
||||
) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>;
|
||||
|
||||
|
@ -404,7 +404,7 @@ pub trait ImportAll: Renderer {
|
|||
fn import_buffer(
|
||||
&mut self,
|
||||
buffer: &wl_buffer::WlBuffer,
|
||||
surface: Option<&SurfaceAttributes>,
|
||||
surface: Option<&crate::wayland::compositor::SurfaceData>,
|
||||
damage: &[Rectangle],
|
||||
) -> Option<Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>>;
|
||||
}
|
||||
|
@ -419,7 +419,7 @@ impl<R: Renderer + ImportShm + ImportEgl + ImportDma> ImportAll for R {
|
|||
fn import_buffer(
|
||||
&mut self,
|
||||
buffer: &wl_buffer::WlBuffer,
|
||||
surface: Option<&SurfaceAttributes>,
|
||||
surface: Option<&SurfaceData>,
|
||||
damage: &[Rectangle],
|
||||
) -> Option<Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>> {
|
||||
match buffer_type(buffer) {
|
||||
|
@ -439,7 +439,7 @@ impl<R: Renderer + ImportShm + ImportDma> ImportAll for R {
|
|||
fn import_buffer(
|
||||
&mut self,
|
||||
buffer: &wl_buffer::WlBuffer,
|
||||
surface: Option<&SurfaceAttributes>,
|
||||
surface: Option<&SurfaceData>,
|
||||
damage: &[Rectangle],
|
||||
) -> Option<Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>> {
|
||||
match buffer_type(buffer) {
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
// The caching logic is used to process surface synchronization. It creates
|
||||
// an effective decoupling between the moment the client sends wl_surface.commit
|
||||
// and the moment where the state that was commited is actually applied by the
|
||||
// compositor.
|
||||
//
|
||||
// The way this is modelled in Smithay is through the `Cache` type, which is a container
|
||||
// representing a cached state for a particular type. The full cached state of a surface
|
||||
// is thus composed of a a set of `Cache<T>` for all relevant `T`, as modelled by the
|
||||
// `MultiCache`.
|
||||
//
|
||||
// The logic of the `Cache` is as follows:
|
||||
//
|
||||
// - The protocol handlers mutably access the `pending` state to modify it accord to
|
||||
// the client requests
|
||||
// - On commit, a snapshot of this pending state is created by invoking `Cacheable::commit`
|
||||
// and stored in the cache alongside an externally provided id
|
||||
// - When the compositor decices that a given state (represented by its commit id) should
|
||||
// become active, `Cache::apply_state` is invoked with that commit id. The associated state
|
||||
// is then applied to the `current` state, that the compositor can then use as a reference
|
||||
// for the current window state. Note that, to preserve the commit ordering, all states
|
||||
// with a commit id older than the one requested are applied as well, in order.
|
||||
//
|
||||
// The logic for generating these commit ids and deciding when to apply them is implemented
|
||||
// and described in `transaction.rs`.
|
||||
|
||||
use std::{
|
||||
cell::{RefCell, RefMut},
|
||||
collections::VecDeque,
|
||||
};
|
||||
|
||||
use downcast_rs::{impl_downcast, Downcast};
|
||||
|
||||
use crate::wayland::Serial;
|
||||
|
||||
/// Trait represening a value that can be used in double-buffered storage
|
||||
///
|
||||
/// The type needs to implement the [`Default`] trait, which will be used
|
||||
/// to initialize. You further need to provide two methods:
|
||||
/// [`Cacheable::commit`] and [`Cacheable::merge_into`].
|
||||
///
|
||||
/// Double-buffered state works by having a "pending" instance of your type,
|
||||
/// into which new values provided by the client are inserted. When the client
|
||||
/// sends `wl_surface.commit`, the [`Cacheable::commit`] method will be
|
||||
/// invoked on your value. This method is expected to produce a new instance of
|
||||
/// your type, that will be stored in the cache, and eventually merged into the
|
||||
/// current state.
|
||||
///
|
||||
/// In most cases, this method will simply produce a copy of the pending state,
|
||||
/// but you might need additionnal logic in some cases, such as for handling
|
||||
/// non-cloneable resources (which thus need to be moved into the produce value).
|
||||
///
|
||||
/// Then at some point the [`Cacheable::merge_into`] method of your type will be
|
||||
/// invoked. In this method, `self` acts as the update that should be merged into
|
||||
/// the current state provided as argument. In simple cases, the action would just
|
||||
/// be to copy `self` into the current state, but mre complex cases require
|
||||
/// additionnal logic.
|
||||
pub trait Cacheable: Default {
|
||||
/// Produce a new state to be cached from the pending state
|
||||
fn commit(&mut self) -> Self;
|
||||
/// Merge a state update into the current state
|
||||
fn merge_into(self, into: &mut Self);
|
||||
}
|
||||
|
||||
struct CachedState<T> {
|
||||
pending: T,
|
||||
cache: VecDeque<(Serial, T)>,
|
||||
current: T,
|
||||
}
|
||||
|
||||
impl<T: Default> Default for CachedState<T> {
|
||||
fn default() -> Self {
|
||||
CachedState {
|
||||
pending: T::default(),
|
||||
cache: VecDeque::new(),
|
||||
current: T::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait Cache: Downcast {
|
||||
fn commit(&self, commit_id: Option<Serial>);
|
||||
fn apply_state(&self, commit_id: Serial);
|
||||
}
|
||||
|
||||
impl_downcast!(Cache);
|
||||
|
||||
impl<T: Cacheable + 'static> Cache for RefCell<CachedState<T>> {
|
||||
fn commit(&self, commit_id: Option<Serial>) {
|
||||
let mut guard = self.borrow_mut();
|
||||
let me = &mut *guard;
|
||||
let new_state = me.pending.commit();
|
||||
if let Some(id) = commit_id {
|
||||
match me.cache.back_mut() {
|
||||
Some(&mut (cid, ref mut state)) if cid == id => new_state.merge_into(state),
|
||||
_ => me.cache.push_back((id, new_state)),
|
||||
}
|
||||
} else {
|
||||
for (_, state) in me.cache.drain(..) {
|
||||
state.merge_into(&mut me.current);
|
||||
}
|
||||
new_state.merge_into(&mut me.current);
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_state(&self, commit_id: Serial) {
|
||||
let mut me = self.borrow_mut();
|
||||
loop {
|
||||
if me.cache.front().map(|&(s, _)| s > commit_id).unwrap_or(true) {
|
||||
// if the cache is empty or the next state has a commit_id greater than the requested one
|
||||
break;
|
||||
}
|
||||
me.cache.pop_front().unwrap().1.merge_into(&mut me.current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A typemap-like container for double-buffered values
|
||||
///
|
||||
/// All values inserted into this container must implement the [`Cacheable`] trait,
|
||||
/// which defines their buffering semantics. They futhermore must be `Send` as the surface state
|
||||
/// can be accessed from multiple threads (but `Sync` is not required, the surface internally synchronizes
|
||||
/// access to its state).
|
||||
///
|
||||
/// Consumers of surface state (like compositor applications using Smithay) will mostly be concerned
|
||||
/// with the [`MultiCache::current`] method, which gives access to the current state of the surface for
|
||||
/// a particular type.
|
||||
///
|
||||
/// Writers of protocol extensions logic will mostly be concerned with the [`MultiCache::pending`] method,
|
||||
/// which provides access to the pending state of the surface, in which new state from clients will be
|
||||
/// stored.
|
||||
///
|
||||
/// This contained has [`RefCell`]-like semantics: values of multiple stored types can be accessed at the
|
||||
/// same time. The stored values are initialized lazyly the first time `current()` or `pending()` are
|
||||
/// invoked with this type as argument.
|
||||
pub struct MultiCache {
|
||||
caches: appendlist::AppendList<Box<dyn Cache + Send>>,
|
||||
}
|
||||
|
||||
impl MultiCache {
|
||||
pub(crate) fn new() -> MultiCache {
|
||||
MultiCache {
|
||||
caches: appendlist::AppendList::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn find_or_insert<T: Cacheable + Send + 'static>(&self) -> &RefCell<CachedState<T>> {
|
||||
for cache in &self.caches {
|
||||
if let Some(v) = (**cache).as_any().downcast_ref() {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
// if we reach here, then the value is not yet in the list, insert it
|
||||
self.caches
|
||||
.push(Box::new(RefCell::new(CachedState::<T>::default())) as Box<_>);
|
||||
(*self.caches[self.caches.len() - 1])
|
||||
.as_any()
|
||||
.downcast_ref()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Acces the pending state associated with type `T`
|
||||
pub fn pending<T: Cacheable + Send + 'static>(&self) -> RefMut<'_, T> {
|
||||
RefMut::map(self.find_or_insert::<T>().borrow_mut(), |cs| &mut cs.pending)
|
||||
}
|
||||
|
||||
/// Access the current state associated with type `T`
|
||||
pub fn current<T: Cacheable + Send + 'static>(&self) -> RefMut<'_, T> {
|
||||
RefMut::map(self.find_or_insert::<T>().borrow_mut(), |cs| &mut cs.current)
|
||||
}
|
||||
|
||||
/// Check if the container currently contains values for type `T`
|
||||
pub fn has<T: Cacheable + Send + 'static>(&self) -> bool {
|
||||
self.caches.iter().any(|c| c.as_any().is::<T>())
|
||||
}
|
||||
|
||||
/// Commits the pending state, invoking Cacheable::commit()
|
||||
///
|
||||
/// If commit_id is None, then the pending state is directly merged
|
||||
/// into the current state. Otherwise, this id is used to store the
|
||||
/// cached state. An id ca no longer be re-used as soon as not new id
|
||||
/// has been used in between. Provided IDs are expected to be provided
|
||||
/// in increasing order according to `Serial` semantics.
|
||||
///
|
||||
/// If a None commit is given but there are some cached states, they'll
|
||||
/// all be merged into the current state before merging the pending one.
|
||||
pub(crate) fn commit(&mut self, commit_id: Option<Serial>) {
|
||||
// none of the underlying borrow_mut() can panic, as we hold
|
||||
// a &mut reference to the container, non are borrowed.
|
||||
for cache in &self.caches {
|
||||
cache.commit(commit_id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply given identified cached state to the current one
|
||||
///
|
||||
/// All other preceding states are applied as well, to preserve commit ordering
|
||||
pub(crate) fn apply_state(&self, commit_id: Serial) {
|
||||
// none of the underlying borrow_mut() can panic, as we hold
|
||||
// a &mut reference to the container, non are borrowed.
|
||||
for cache in &self.caches {
|
||||
cache.apply_state(commit_id);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,12 @@
|
|||
use std::{cell::RefCell, ops::Deref as _, rc::Rc, sync::Mutex};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
ops::Deref as _,
|
||||
rc::Rc,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Mutex,
|
||||
},
|
||||
};
|
||||
|
||||
use wayland_server::{
|
||||
protocol::{wl_compositor, wl_region, wl_subcompositor, wl_subsurface, wl_surface},
|
||||
|
@ -6,23 +14,22 @@ use wayland_server::{
|
|||
};
|
||||
|
||||
use super::{
|
||||
tree::{Location, SurfaceData},
|
||||
AlreadyHasRole, BufferAssignment, CompositorToken, Damage, Rectangle, RectangleKind, RegionAttributes,
|
||||
Role, RoleType, SubsurfaceRole, SurfaceEvent,
|
||||
cache::Cacheable,
|
||||
tree::{Location, PrivateSurfaceData},
|
||||
AlreadyHasRole, BufferAssignment, Damage, Rectangle, RectangleKind, RegionAttributes, SurfaceAttributes,
|
||||
};
|
||||
|
||||
/*
|
||||
* wl_compositor
|
||||
*/
|
||||
|
||||
pub(crate) fn implement_compositor<R, Impl>(
|
||||
pub(crate) fn implement_compositor<Impl>(
|
||||
compositor: Main<wl_compositor::WlCompositor>,
|
||||
log: ::slog::Logger,
|
||||
implem: Rc<RefCell<Impl>>,
|
||||
) -> wl_compositor::WlCompositor
|
||||
where
|
||||
R: Default + Send + 'static,
|
||||
Impl: for<'a> FnMut(SurfaceEvent, wl_surface::WlSurface, CompositorToken<R>, DispatchData<'a>) + 'static,
|
||||
Impl: for<'a> FnMut(wl_surface::WlSurface, DispatchData<'a>) + 'static,
|
||||
{
|
||||
compositor.quick_assign(move |_compositor, request, _| match request {
|
||||
wl_compositor::Request::CreateSurface { id } => {
|
||||
|
@ -42,29 +49,24 @@ where
|
|||
* wl_surface
|
||||
*/
|
||||
|
||||
type SurfaceImplemFn<R> =
|
||||
dyn for<'a> FnMut(SurfaceEvent, wl_surface::WlSurface, CompositorToken<R>, DispatchData<'a>);
|
||||
type SurfaceImplemFn = dyn for<'a> FnMut(wl_surface::WlSurface, DispatchData<'a>);
|
||||
|
||||
// Internal implementation data of surfaces
|
||||
pub(crate) struct SurfaceImplem<R> {
|
||||
pub(crate) struct SurfaceImplem {
|
||||
log: ::slog::Logger,
|
||||
implem: Rc<RefCell<SurfaceImplemFn<R>>>,
|
||||
implem: Rc<RefCell<SurfaceImplemFn>>,
|
||||
}
|
||||
|
||||
impl<R> SurfaceImplem<R> {
|
||||
fn make<Impl>(log: ::slog::Logger, implem: Rc<RefCell<Impl>>) -> SurfaceImplem<R>
|
||||
impl SurfaceImplem {
|
||||
fn make<Impl>(log: ::slog::Logger, implem: Rc<RefCell<Impl>>) -> SurfaceImplem
|
||||
where
|
||||
Impl: for<'a> FnMut(SurfaceEvent, wl_surface::WlSurface, CompositorToken<R>, DispatchData<'a>)
|
||||
+ 'static,
|
||||
Impl: for<'a> FnMut(wl_surface::WlSurface, DispatchData<'a>) + 'static,
|
||||
{
|
||||
SurfaceImplem { log, implem }
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> SurfaceImplem<R>
|
||||
where
|
||||
R: 'static,
|
||||
{
|
||||
impl SurfaceImplem {
|
||||
fn receive_surface_request(
|
||||
&mut self,
|
||||
req: wl_surface::Request,
|
||||
|
@ -73,8 +75,8 @@ where
|
|||
) {
|
||||
match req {
|
||||
wl_surface::Request::Attach { buffer, x, y } => {
|
||||
SurfaceData::<R>::with_data(&surface, |d| {
|
||||
d.buffer = Some(match buffer {
|
||||
PrivateSurfaceData::with_states(&surface, |states| {
|
||||
states.cached_state.pending::<SurfaceAttributes>().buffer = Some(match buffer {
|
||||
Some(buffer) => BufferAssignment::NewBuffer {
|
||||
buffer,
|
||||
delta: (x, y),
|
||||
|
@ -84,13 +86,21 @@ where
|
|||
});
|
||||
}
|
||||
wl_surface::Request::Damage { x, y, width, height } => {
|
||||
SurfaceData::<R>::with_data(&surface, |d| {
|
||||
d.damage.push(Damage::Surface(Rectangle { x, y, width, height }));
|
||||
PrivateSurfaceData::with_states(&surface, |states| {
|
||||
states
|
||||
.cached_state
|
||||
.pending::<SurfaceAttributes>()
|
||||
.damage
|
||||
.push(Damage::Surface(Rectangle { x, y, width, height }));
|
||||
});
|
||||
}
|
||||
wl_surface::Request::Frame { callback } => {
|
||||
SurfaceData::<R>::with_data(&surface, move |d| {
|
||||
d.frame_callbacks.push((*callback).clone());
|
||||
PrivateSurfaceData::with_states(&surface, |states| {
|
||||
states
|
||||
.cached_state
|
||||
.pending::<SurfaceAttributes>()
|
||||
.frame_callbacks
|
||||
.push((*callback).clone());
|
||||
});
|
||||
}
|
||||
wl_surface::Request::SetOpaqueRegion { region } => {
|
||||
|
@ -98,29 +108,50 @@ where
|
|||
let attributes_mutex = r.as_ref().user_data().get::<Mutex<RegionAttributes>>().unwrap();
|
||||
attributes_mutex.lock().unwrap().clone()
|
||||
});
|
||||
SurfaceData::<R>::with_data(&surface, |d| d.opaque_region = attributes);
|
||||
PrivateSurfaceData::with_states(&surface, |states| {
|
||||
states.cached_state.pending::<SurfaceAttributes>().opaque_region = attributes;
|
||||
});
|
||||
}
|
||||
wl_surface::Request::SetInputRegion { region } => {
|
||||
let attributes = region.map(|r| {
|
||||
let attributes_mutex = r.as_ref().user_data().get::<Mutex<RegionAttributes>>().unwrap();
|
||||
attributes_mutex.lock().unwrap().clone()
|
||||
});
|
||||
SurfaceData::<R>::with_data(&surface, |d| d.input_region = attributes);
|
||||
PrivateSurfaceData::with_states(&surface, |states| {
|
||||
states.cached_state.pending::<SurfaceAttributes>().input_region = attributes;
|
||||
});
|
||||
}
|
||||
wl_surface::Request::Commit => {
|
||||
let mut user_impl = self.implem.borrow_mut();
|
||||
PrivateSurfaceData::invoke_commit_hooks(&surface);
|
||||
if !surface.as_ref().is_alive() {
|
||||
// the client was killed by a hook, abort
|
||||
return;
|
||||
}
|
||||
PrivateSurfaceData::commit(&surface);
|
||||
trace!(self.log, "Calling user implementation for wl_surface.commit");
|
||||
(&mut *user_impl)(SurfaceEvent::Commit, surface, CompositorToken::make(), ddata);
|
||||
(&mut *user_impl)(surface, ddata);
|
||||
}
|
||||
wl_surface::Request::SetBufferTransform { transform } => {
|
||||
SurfaceData::<R>::with_data(&surface, |d| d.buffer_transform = transform);
|
||||
PrivateSurfaceData::with_states(&surface, |states| {
|
||||
states
|
||||
.cached_state
|
||||
.pending::<SurfaceAttributes>()
|
||||
.buffer_transform = transform;
|
||||
});
|
||||
}
|
||||
wl_surface::Request::SetBufferScale { scale } => {
|
||||
SurfaceData::<R>::with_data(&surface, |d| d.buffer_scale = scale);
|
||||
PrivateSurfaceData::with_states(&surface, |states| {
|
||||
states.cached_state.pending::<SurfaceAttributes>().buffer_scale = scale;
|
||||
});
|
||||
}
|
||||
wl_surface::Request::DamageBuffer { x, y, width, height } => {
|
||||
SurfaceData::<R>::with_data(&surface, |d| {
|
||||
d.damage.push(Damage::Buffer(Rectangle { x, y, width, height }))
|
||||
PrivateSurfaceData::with_states(&surface, |states| {
|
||||
states
|
||||
.cached_state
|
||||
.pending::<SurfaceAttributes>()
|
||||
.damage
|
||||
.push(Damage::Buffer(Rectangle { x, y, width, height }))
|
||||
});
|
||||
}
|
||||
wl_surface::Request::Destroy => {
|
||||
|
@ -131,22 +162,53 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn implement_surface<R, Impl>(
|
||||
impl Cacheable for SurfaceAttributes {
|
||||
fn commit(&mut self) -> Self {
|
||||
SurfaceAttributes {
|
||||
buffer: self.buffer.take(),
|
||||
buffer_scale: self.buffer_scale,
|
||||
buffer_transform: self.buffer_transform,
|
||||
damage: std::mem::take(&mut self.damage),
|
||||
opaque_region: self.opaque_region.clone(),
|
||||
input_region: self.input_region.clone(),
|
||||
frame_callbacks: std::mem::take(&mut self.frame_callbacks),
|
||||
}
|
||||
}
|
||||
fn merge_into(self, into: &mut Self) {
|
||||
if self.buffer.is_some() {
|
||||
if let Some(BufferAssignment::NewBuffer { buffer, .. }) =
|
||||
std::mem::replace(&mut into.buffer, self.buffer)
|
||||
{
|
||||
buffer.release();
|
||||
}
|
||||
}
|
||||
into.buffer_scale = self.buffer_scale;
|
||||
into.buffer_transform = self.buffer_transform;
|
||||
into.damage.extend(self.damage);
|
||||
into.opaque_region = self.opaque_region;
|
||||
into.input_region = self.input_region;
|
||||
into.frame_callbacks.extend(self.frame_callbacks);
|
||||
}
|
||||
}
|
||||
|
||||
fn implement_surface<Impl>(
|
||||
surface: Main<wl_surface::WlSurface>,
|
||||
log: ::slog::Logger,
|
||||
implem: Rc<RefCell<Impl>>,
|
||||
) -> wl_surface::WlSurface
|
||||
where
|
||||
R: Default + Send + 'static,
|
||||
Impl: for<'a> FnMut(SurfaceEvent, wl_surface::WlSurface, CompositorToken<R>, DispatchData<'a>) + 'static,
|
||||
Impl: for<'a> FnMut(wl_surface::WlSurface, DispatchData<'a>) + 'static,
|
||||
{
|
||||
surface.quick_assign({
|
||||
let mut implem = SurfaceImplem::make(log, implem);
|
||||
move |surface, req, ddata| implem.receive_surface_request(req, surface.deref().clone(), ddata)
|
||||
});
|
||||
surface.assign_destructor(Filter::new(|surface, _, _| SurfaceData::<R>::cleanup(&surface)));
|
||||
surface.as_ref().user_data().set_threadsafe(SurfaceData::<R>::new);
|
||||
SurfaceData::<R>::init(&surface);
|
||||
surface.assign_destructor(Filter::new(|surface, _, _| PrivateSurfaceData::cleanup(&surface)));
|
||||
surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.set_threadsafe(PrivateSurfaceData::new);
|
||||
PrivateSurfaceData::init(&surface);
|
||||
surface.deref().clone()
|
||||
}
|
||||
|
||||
|
@ -188,22 +250,19 @@ fn implement_region(region: Main<wl_region::WlRegion>) -> wl_region::WlRegion {
|
|||
* wl_subcompositor
|
||||
*/
|
||||
|
||||
pub(crate) fn implement_subcompositor<R>(
|
||||
pub(crate) fn implement_subcompositor(
|
||||
subcompositor: Main<wl_subcompositor::WlSubcompositor>,
|
||||
) -> wl_subcompositor::WlSubcompositor
|
||||
where
|
||||
R: RoleType + Role<SubsurfaceRole> + 'static,
|
||||
{
|
||||
) -> wl_subcompositor::WlSubcompositor {
|
||||
subcompositor.quick_assign(move |subcompositor, request, _| match request {
|
||||
wl_subcompositor::Request::GetSubsurface { id, surface, parent } => {
|
||||
if let Err(AlreadyHasRole) = SurfaceData::<R>::set_parent(&surface, &parent) {
|
||||
if let Err(AlreadyHasRole) = PrivateSurfaceData::set_parent(&surface, &parent) {
|
||||
subcompositor.as_ref().post_error(
|
||||
wl_subcompositor::Error::BadSurface as u32,
|
||||
"Surface already has a role.".into(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
implement_subsurface::<R>(id, surface);
|
||||
implement_subsurface(id, surface);
|
||||
}
|
||||
wl_subcompositor::Request::Destroy => {}
|
||||
_ => unreachable!(),
|
||||
|
@ -215,32 +274,72 @@ where
|
|||
* wl_subsurface
|
||||
*/
|
||||
|
||||
fn with_subsurface_attributes<R, F>(subsurface: &wl_subsurface::WlSubsurface, f: F)
|
||||
where
|
||||
F: FnOnce(&mut SubsurfaceRole),
|
||||
R: RoleType + Role<SubsurfaceRole> + 'static,
|
||||
{
|
||||
let surface = subsurface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<wl_surface::WlSurface>()
|
||||
.unwrap();
|
||||
SurfaceData::<R>::with_role_data::<SubsurfaceRole, _, _>(surface, |d| f(d))
|
||||
.expect("The surface does not have a subsurface role while it has a wl_subsurface?!");
|
||||
/// The cached state associated with a subsurface
|
||||
pub struct SubsurfaceCachedState {
|
||||
/// Location of the top-left corner of this subsurface
|
||||
/// relative to its parent coordinate space
|
||||
pub location: (i32, i32),
|
||||
}
|
||||
|
||||
fn implement_subsurface<R>(
|
||||
impl Default for SubsurfaceCachedState {
|
||||
fn default() -> Self {
|
||||
SubsurfaceCachedState { location: (0, 0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Cacheable for SubsurfaceCachedState {
|
||||
fn commit(&mut self) -> Self {
|
||||
SubsurfaceCachedState {
|
||||
location: self.location,
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_into(self, into: &mut Self) {
|
||||
into.location = self.location;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SubsurfaceState {
|
||||
pub(crate) sync: AtomicBool,
|
||||
}
|
||||
|
||||
impl SubsurfaceState {
|
||||
fn new() -> SubsurfaceState {
|
||||
SubsurfaceState {
|
||||
sync: AtomicBool::new(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a (sub)surface is effectively sync
|
||||
pub fn is_effectively_sync(surface: &wl_surface::WlSurface) -> bool {
|
||||
let is_direct_sync = PrivateSurfaceData::with_states(surface, |state| {
|
||||
state
|
||||
.data_map
|
||||
.get::<SubsurfaceState>()
|
||||
.map(|s| s.sync.load(Ordering::Acquire))
|
||||
.unwrap_or(false)
|
||||
});
|
||||
if is_direct_sync {
|
||||
return true;
|
||||
}
|
||||
if let Some(parent) = PrivateSurfaceData::get_parent(surface) {
|
||||
is_effectively_sync(&parent)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn implement_subsurface(
|
||||
subsurface: Main<wl_subsurface::WlSubsurface>,
|
||||
surface: wl_surface::WlSurface,
|
||||
) -> wl_subsurface::WlSubsurface
|
||||
where
|
||||
R: RoleType + Role<SubsurfaceRole> + 'static,
|
||||
{
|
||||
subsurface.quick_assign(|subsurface, request, _| {
|
||||
) -> wl_subsurface::WlSubsurface {
|
||||
let data_surface = surface.clone();
|
||||
subsurface.quick_assign(move |subsurface, request, _| {
|
||||
match request {
|
||||
wl_subsurface::Request::SetPosition { x, y } => {
|
||||
with_subsurface_attributes::<R, _>(&subsurface, |attrs| {
|
||||
attrs.location = (x, y);
|
||||
PrivateSurfaceData::with_states(&surface, |state| {
|
||||
state.cached_state.pending::<SubsurfaceCachedState>().location = (x, y);
|
||||
})
|
||||
}
|
||||
wl_subsurface::Request::PlaceAbove { sibling } => {
|
||||
|
@ -249,7 +348,7 @@ where
|
|||
.user_data()
|
||||
.get::<wl_surface::WlSurface>()
|
||||
.unwrap();
|
||||
if let Err(()) = SurfaceData::<R>::reorder(surface, Location::After, &sibling) {
|
||||
if let Err(()) = PrivateSurfaceData::reorder(surface, Location::After, &sibling) {
|
||||
subsurface.as_ref().post_error(
|
||||
wl_subsurface::Error::BadSurface as u32,
|
||||
"Provided surface is not a sibling or parent.".into(),
|
||||
|
@ -262,18 +361,28 @@ where
|
|||
.user_data()
|
||||
.get::<wl_surface::WlSurface>()
|
||||
.unwrap();
|
||||
if let Err(()) = SurfaceData::<R>::reorder(surface, Location::Before, &sibling) {
|
||||
if let Err(()) = PrivateSurfaceData::reorder(surface, Location::Before, &sibling) {
|
||||
subsurface.as_ref().post_error(
|
||||
wl_subsurface::Error::BadSurface as u32,
|
||||
"Provided surface is not a sibling or parent.".into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
wl_subsurface::Request::SetSync => with_subsurface_attributes::<R, _>(&subsurface, |attrs| {
|
||||
attrs.sync = true;
|
||||
wl_subsurface::Request::SetSync => PrivateSurfaceData::with_states(&surface, |state| {
|
||||
state
|
||||
.data_map
|
||||
.get::<SubsurfaceState>()
|
||||
.unwrap()
|
||||
.sync
|
||||
.store(true, Ordering::Release);
|
||||
}),
|
||||
wl_subsurface::Request::SetDesync => with_subsurface_attributes::<R, _>(&subsurface, |attrs| {
|
||||
attrs.sync = false;
|
||||
wl_subsurface::Request::SetDesync => PrivateSurfaceData::with_states(&surface, |state| {
|
||||
state
|
||||
.data_map
|
||||
.get::<SubsurfaceState>()
|
||||
.unwrap()
|
||||
.sync
|
||||
.store(false, Ordering::Release);
|
||||
}),
|
||||
wl_subsurface::Request::Destroy => {
|
||||
// Our destructor already handles it
|
||||
|
@ -281,23 +390,25 @@ where
|
|||
_ => unreachable!(),
|
||||
}
|
||||
});
|
||||
subsurface.assign_destructor(Filter::new(|subsurface, _, _| {
|
||||
destroy_subsurface::<R>(&subsurface)
|
||||
}));
|
||||
subsurface.as_ref().user_data().set_threadsafe(|| surface);
|
||||
super::with_states(&data_surface, |states| {
|
||||
states.data_map.insert_if_missing_threadsafe(SubsurfaceState::new)
|
||||
})
|
||||
.unwrap();
|
||||
subsurface.assign_destructor(Filter::new(|subsurface, _, _| destroy_subsurface(&subsurface)));
|
||||
subsurface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.set_threadsafe(move || data_surface);
|
||||
subsurface.deref().clone()
|
||||
}
|
||||
|
||||
fn destroy_subsurface<R>(subsurface: &wl_subsurface::WlSubsurface)
|
||||
where
|
||||
R: RoleType + Role<SubsurfaceRole> + 'static,
|
||||
{
|
||||
fn destroy_subsurface(subsurface: &wl_subsurface::WlSubsurface) {
|
||||
let surface = subsurface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<wl_surface::WlSurface>()
|
||||
.unwrap();
|
||||
if surface.as_ref().is_alive() {
|
||||
SurfaceData::<R>::unset_parent(&surface);
|
||||
PrivateSurfaceData::unset_parent(&surface);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
//!
|
||||
//! This implementation does a simple job: it stores in a coherent way the state of
|
||||
//! surface trees with subsurfaces, to provide you a direct access to the tree
|
||||
//! structure and all surface metadata.
|
||||
//! structure and all surface attributes, and handles the application of double-buffered
|
||||
//! state.
|
||||
//!
|
||||
//! As such, you can, given a root surface with a role requiring it to be displayed,
|
||||
//! you can iterate over the whole tree of subsurfaces to recover all the metadata you
|
||||
|
@ -32,54 +33,67 @@
|
|||
//! # #[macro_use] extern crate smithay;
|
||||
//! use smithay::wayland::compositor::compositor_init;
|
||||
//!
|
||||
//! // Declare the roles enum
|
||||
//! define_roles!(MyRoles);
|
||||
//!
|
||||
//! # let mut display = wayland_server::Display::new();
|
||||
//! // Call the init function:
|
||||
//! let (token, _, _) = compositor_init::<MyRoles, _, _>(
|
||||
//! compositor_init(
|
||||
//! &mut display,
|
||||
//! |request, surface, compositor_token, dispatch_data| {
|
||||
//! |surface, dispatch_data| {
|
||||
//! /*
|
||||
//! Your handling of the user requests.
|
||||
//! Your handling of surface commits
|
||||
//! */
|
||||
//! },
|
||||
//! None // put a logger here
|
||||
//! );
|
||||
//!
|
||||
//! // this `token` is what you'll use to access the surface associated data
|
||||
//!
|
||||
//! // You're now ready to go!
|
||||
//! ```
|
||||
//!
|
||||
//! ### Use the surface metadata
|
||||
//! ### Use the surface states
|
||||
//!
|
||||
//! As you can see in the previous example, in the end we are retrieving a token from
|
||||
//! the `init` function. This token is necessary to retrieve the metadata associated with
|
||||
//! a surface. It can be cloned. See [`CompositorToken`]
|
||||
//! for the details of what it enables you.
|
||||
//! The main access to surface states is done through the [`with_states`] function, which
|
||||
//! gives you access to the [`SurfaceData`] instance associated with this surface. It acts
|
||||
//! as a general purpose container for associating state to a surface, double-buffered or
|
||||
//! not. See its documentation for more details.
|
||||
//!
|
||||
//! The surface metadata is held in the [`SurfaceAttributes`]
|
||||
//! struct. In contains double-buffered state pending from the client as defined by the protocol for
|
||||
//! [`wl_surface`](wayland_server::protocol::wl_surface), as well as your user-defined type holding
|
||||
//! any data you need to have associated with a struct. See its documentation for details.
|
||||
//! ### State application and hooks
|
||||
//!
|
||||
//! This [`CompositorToken`] also provides access to the metadata associated with the role of the
|
||||
//! surfaces. See the documentation of the [`roles`] submodule
|
||||
//! for a detailed explanation.
|
||||
//! On commit of a surface several steps are taken to update the state of the surface. Actions
|
||||
//! are taken by smithay in the following order:
|
||||
//!
|
||||
//! 1. Commit hooks registered to this surface are invoked. Such hooks can be registered using
|
||||
//! the [`add_commit_hook`] function. They are typically used by protocol extensions that
|
||||
//! add state to a surface and need to check on commit that client did not request an
|
||||
//! illegal state before it is applied on commit.
|
||||
//! 2. The pending state is either applied and made current, or cached for later application
|
||||
//! is the surface is a synchronize subsurface. If the current state is applied, state
|
||||
//! of the synchronized children subsurface are applied as well at this point.
|
||||
//! 3. Your user callback provided to [`compositor_init`] is invoked, so that you can access
|
||||
//! the new current state of the surface. The state of sync children subsurfaces of your
|
||||
//! surface may have changed as well, so this is the place to check it, using functions
|
||||
//! like [`with_surface_tree_upward`] or [`with_surface_tree_downward`]. On the other hand,
|
||||
//! if the surface is a sync subsurface, its current state will note have changed as
|
||||
//! the result of that commit. You can check if it is using [`is_sync_subsurface`].
|
||||
//!
|
||||
//! ### Surface roles
|
||||
//!
|
||||
//! The wayland protocol specifies that a surface needs to be assigned a role before it can
|
||||
//! be displayed. Furthermore, a surface can only have a single role during its whole lifetime.
|
||||
//! Smithay represents this role as a `&'static str` identifier, that can only be set once
|
||||
//! on a surface. See [`give_role`] and [`get_role`] for details. This module manages the
|
||||
//! subsurface role, which is identified by the string `"subsurface"`.
|
||||
|
||||
use std::{cell::RefCell, fmt, rc::Rc, sync::Mutex};
|
||||
use std::{cell::RefCell, rc::Rc, sync::Mutex};
|
||||
|
||||
mod cache;
|
||||
mod handlers;
|
||||
pub mod roles;
|
||||
mod transaction;
|
||||
mod tree;
|
||||
|
||||
pub use self::tree::TraversalAction;
|
||||
use self::{
|
||||
roles::{AlreadyHasRole, Role, RoleType, WrongRole},
|
||||
tree::SurfaceData,
|
||||
};
|
||||
use crate::utils::Rectangle;
|
||||
pub use self::cache::{Cacheable, MultiCache};
|
||||
pub use self::handlers::SubsurfaceCachedState;
|
||||
use self::tree::PrivateSurfaceData;
|
||||
pub use self::tree::{AlreadyHasRole, TraversalAction};
|
||||
use crate::utils::{DeadResource, Rectangle};
|
||||
use wayland_server::{
|
||||
protocol::{
|
||||
wl_buffer, wl_callback, wl_compositor, wl_output, wl_region, wl_subcompositor, wl_surface::WlSurface,
|
||||
|
@ -104,6 +118,32 @@ struct Marker<R> {
|
|||
_r: ::std::marker::PhantomData<R>,
|
||||
}
|
||||
|
||||
/// The state container associated with a surface
|
||||
///
|
||||
/// This general-purpose container provides 2 main storages:
|
||||
///
|
||||
/// - the `data_map` storage has typemap semantics and allows you
|
||||
/// to associate and access non-buffered data to the surface
|
||||
/// - the `cached_state` storages allows you to associate state to
|
||||
/// the surface that follows the double-buffering semantics associated
|
||||
/// with the `commit` procedure of surfaces, also with typemap-like
|
||||
/// semantics
|
||||
///
|
||||
/// See the respective documentation of each container for its usage.
|
||||
///
|
||||
/// By default, all surfaces have a [`SurfaceAttributes`] cached state,
|
||||
/// and subsurface also have a [`SubsurfaceCachedState`] state as well.
|
||||
pub struct SurfaceData {
|
||||
/// The current role of the surface.
|
||||
///
|
||||
/// If `None` if the surface has not yet been assigned a role
|
||||
pub role: Option<&'static str>,
|
||||
/// The non-buffered typemap storage of this surface
|
||||
pub data_map: UserDataMap,
|
||||
/// The double-buffered typemap storage of this surface
|
||||
pub cached_state: MultiCache,
|
||||
}
|
||||
|
||||
/// New buffer assignation for a surface
|
||||
#[derive(Debug)]
|
||||
pub enum BufferAssignment {
|
||||
|
@ -118,14 +158,12 @@ pub enum BufferAssignment {
|
|||
},
|
||||
}
|
||||
|
||||
/// Data associated with a surface, aggregated by the handlers
|
||||
/// General state associated with a surface
|
||||
///
|
||||
/// Most of the fields of this struct represent a double-buffered state, which
|
||||
/// should only be applied once a [`commit`](SurfaceEvent::Commit)
|
||||
/// request is received from the surface.
|
||||
///
|
||||
/// You are responsible for setting those values as you see fit to avoid
|
||||
/// processing them two times.
|
||||
/// The fields `buffer`, `damage` and `frame_callbacks` should be
|
||||
/// reset (by clearing their contents) once you have adequately
|
||||
/// processed them, as their contents are aggregated from commit to commit.
|
||||
#[derive(Debug)]
|
||||
pub struct SurfaceAttributes {
|
||||
/// Buffer defining the contents of the surface
|
||||
///
|
||||
|
@ -172,26 +210,6 @@ pub struct SurfaceAttributes {
|
|||
/// An example possibility would be to trigger it once the frame
|
||||
/// associated with this commit has been displayed on the screen.
|
||||
pub frame_callbacks: Vec<wl_callback::WlCallback>,
|
||||
/// User-controlled data
|
||||
///
|
||||
/// This is your field to host whatever you need.
|
||||
pub user_data: UserDataMap,
|
||||
}
|
||||
|
||||
// UserDataMap does not implement debug, so we have to impl Debug manually
|
||||
impl fmt::Debug for SurfaceAttributes {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("SurfaceAttributes")
|
||||
.field("buffer", &self.buffer)
|
||||
.field("buffer_scale", &self.buffer_scale)
|
||||
.field("buffer_transform", &self.buffer_transform)
|
||||
.field("opaque_region", &self.opaque_region)
|
||||
.field("input_region", &self.input_region)
|
||||
.field("damage", &self.damage)
|
||||
.field("frame_callbacks", &self.frame_callbacks)
|
||||
.field("user_data", &"...")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SurfaceAttributes {
|
||||
|
@ -204,7 +222,6 @@ impl Default for SurfaceAttributes {
|
|||
input_region: None,
|
||||
damage: Vec::new(),
|
||||
frame_callbacks: Vec::new(),
|
||||
user_data: UserDataMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -277,244 +294,160 @@ impl RegionAttributes {
|
|||
}
|
||||
}
|
||||
|
||||
/// A Compositor global token
|
||||
/// Access the data of a surface tree from bottom to top
|
||||
///
|
||||
/// This token can be cloned at will, and is the entry-point to
|
||||
/// access data associated with the [`wl_surface`](wayland_server::protocol::wl_surface)
|
||||
/// and [`wl_region`](wayland_server::protocol::wl_region) managed
|
||||
/// by the `CompositorGlobal` that provided it.
|
||||
#[derive(Debug)]
|
||||
pub struct CompositorToken<R> {
|
||||
_role: ::std::marker::PhantomData<*mut R>,
|
||||
}
|
||||
|
||||
// we implement them manually because #[derive(..)] would require R: Clone
|
||||
impl<R> Copy for CompositorToken<R> {}
|
||||
impl<R> Clone for CompositorToken<R> {
|
||||
fn clone(&self) -> CompositorToken<R> {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<R> Send for CompositorToken<R> {}
|
||||
unsafe impl<R> Sync for CompositorToken<R> {}
|
||||
|
||||
impl<R> CompositorToken<R> {
|
||||
pub(crate) fn make() -> CompositorToken<R> {
|
||||
CompositorToken {
|
||||
_role: ::std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: 'static> CompositorToken<R> {
|
||||
/// Access the data of a surface
|
||||
///
|
||||
/// The closure will be called with the contents of the data associated with this surface.
|
||||
///
|
||||
/// If the surface is not managed by the `CompositorGlobal` that provided this token, this
|
||||
/// will panic (having more than one compositor is not supported).
|
||||
pub fn with_surface_data<F, T>(self, surface: &WlSurface, f: F) -> T
|
||||
where
|
||||
F: FnOnce(&mut SurfaceAttributes) -> T,
|
||||
{
|
||||
SurfaceData::<R>::with_data(surface, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> CompositorToken<R>
|
||||
where
|
||||
R: RoleType + Role<SubsurfaceRole> + 'static,
|
||||
/// You provide three closures, a "filter", a "processor" and a "post filter".
|
||||
///
|
||||
/// The first closure is initially called on a surface to determine if its children
|
||||
/// should be processed as well. It returns a `TraversalAction<T>` reflecting that.
|
||||
///
|
||||
/// The second closure is supposed to do the actual processing. The processing closure for
|
||||
/// a surface may be called after the processing closure of some of its children, depending
|
||||
/// on the stack ordering the client requested. Here the surfaces are processed in the same
|
||||
/// order as they are supposed to be drawn: from the farthest of the screen to the nearest.
|
||||
///
|
||||
/// The third closure is called once all the subtree of a node has been processed, and gives
|
||||
/// an opportunity for early-stopping. If it returns `true` the processing will continue,
|
||||
/// while if it returns `false` it'll stop.
|
||||
///
|
||||
/// The arguments provided to the closures are, in this order:
|
||||
///
|
||||
/// - The surface object itself
|
||||
/// - a mutable reference to its surface attribute data
|
||||
/// - a mutable reference to its role data,
|
||||
/// - a custom value that is passed in a fold-like manner, but only from the output of a parent
|
||||
/// to its children. See [`TraversalAction`] for details.
|
||||
///
|
||||
/// If the surface not managed by the `CompositorGlobal` that provided this token, this
|
||||
/// will panic (having more than one compositor is not supported).
|
||||
pub fn with_surface_tree_upward<F1, F2, F3, T>(
|
||||
surface: &WlSurface,
|
||||
initial: T,
|
||||
filter: F1,
|
||||
processor: F2,
|
||||
post_filter: F3,
|
||||
) where
|
||||
F1: FnMut(&WlSurface, &SurfaceData, &T) -> TraversalAction<T>,
|
||||
F2: FnMut(&WlSurface, &SurfaceData, &T),
|
||||
F3: FnMut(&WlSurface, &SurfaceData, &T) -> bool,
|
||||
{
|
||||
/// Access the data of a surface tree from bottom to top
|
||||
///
|
||||
/// You provide three closures, a "filter", a "processor" and a "post filter".
|
||||
///
|
||||
/// The first closure is initially called on a surface to determine if its children
|
||||
/// should be processed as well. It returns a `TraversalAction<T>` reflecting that.
|
||||
///
|
||||
/// The second closure is supposed to do the actual processing. The processing closure for
|
||||
/// a surface may be called after the processing closure of some of its children, depending
|
||||
/// on the stack ordering the client requested. Here the surfaces are processed in the same
|
||||
/// order as they are supposed to be drawn: from the farthest of the screen to the nearest.
|
||||
///
|
||||
/// The third closure is called once all the subtree of a node has been processed, and gives
|
||||
/// an opportunity for early-stopping. If it returns `true` the processing will continue,
|
||||
/// while if it returns `false` it'll stop.
|
||||
///
|
||||
/// The arguments provided to the closures are, in this order:
|
||||
///
|
||||
/// - The surface object itself
|
||||
/// - a mutable reference to its surface attribute data
|
||||
/// - a mutable reference to its role data,
|
||||
/// - a custom value that is passed in a fold-like manner, but only from the output of a parent
|
||||
/// to its children. See [`TraversalAction`] for details.
|
||||
///
|
||||
/// If the surface not managed by the `CompositorGlobal` that provided this token, this
|
||||
/// will panic (having more than one compositor is not supported).
|
||||
pub fn with_surface_tree_upward<F1, F2, F3, T>(
|
||||
self,
|
||||
surface: &WlSurface,
|
||||
initial: T,
|
||||
filter: F1,
|
||||
processor: F2,
|
||||
post_filter: F3,
|
||||
) where
|
||||
F1: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction<T>,
|
||||
F2: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T),
|
||||
F3: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T) -> bool,
|
||||
{
|
||||
SurfaceData::<R>::map_tree(surface, &initial, filter, processor, post_filter, false);
|
||||
}
|
||||
|
||||
/// Access the data of a surface tree from top to bottom
|
||||
///
|
||||
/// Behavior is the same as [`with_surface_tree_upward`](CompositorToken::with_surface_tree_upward), but
|
||||
/// the processing is done in the reverse order, from the nearest of the screen to the deepest.
|
||||
///
|
||||
/// This would typically be used to find out which surface of a subsurface tree has been clicked for example.
|
||||
pub fn with_surface_tree_downward<F1, F2, F3, T>(
|
||||
self,
|
||||
surface: &WlSurface,
|
||||
initial: T,
|
||||
filter: F1,
|
||||
processor: F2,
|
||||
post_filter: F3,
|
||||
) where
|
||||
F1: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction<T>,
|
||||
F2: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T),
|
||||
F3: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T) -> bool,
|
||||
{
|
||||
SurfaceData::<R>::map_tree(surface, &initial, filter, processor, post_filter, true);
|
||||
}
|
||||
|
||||
/// Retrieve the parent of this surface
|
||||
///
|
||||
/// Returns `None` is this surface is a root surface
|
||||
///
|
||||
/// If the surface is not managed by the `CompositorGlobal` that provided this token, this
|
||||
/// will panic (having more than one compositor is not supported).
|
||||
pub fn get_parent(self, surface: &WlSurface) -> Option<WlSurface> {
|
||||
SurfaceData::<R>::get_parent(surface)
|
||||
}
|
||||
|
||||
/// Retrieve the children of this surface
|
||||
///
|
||||
/// If the surface is not managed by the `CompositorGlobal` that provided this token, this
|
||||
/// will panic (having more than one compositor is not supported).
|
||||
pub fn get_children(self, surface: &WlSurface) -> Vec<WlSurface> {
|
||||
SurfaceData::<R>::get_children(surface)
|
||||
}
|
||||
PrivateSurfaceData::map_tree(surface, &initial, filter, processor, post_filter, false);
|
||||
}
|
||||
|
||||
impl<R: RoleType + 'static> CompositorToken<R> {
|
||||
/// Check whether this surface as a role or not
|
||||
///
|
||||
/// If the surface is not managed by the `CompositorGlobal` that provided this token, this
|
||||
/// will panic (having more than one compositor is not supported).
|
||||
pub fn has_a_role(self, surface: &WlSurface) -> bool {
|
||||
SurfaceData::<R>::has_a_role(surface)
|
||||
}
|
||||
/// Access the data of a surface tree from top to bottom
|
||||
///
|
||||
/// Behavior is the same as [`with_surface_tree_upward`], but the processing is done in the reverse order,
|
||||
/// from the nearest of the screen to the deepest.
|
||||
///
|
||||
/// This would typically be used to find out which surface of a subsurface tree has been clicked for example.
|
||||
pub fn with_surface_tree_downward<F1, F2, F3, T>(
|
||||
surface: &WlSurface,
|
||||
initial: T,
|
||||
filter: F1,
|
||||
processor: F2,
|
||||
post_filter: F3,
|
||||
) where
|
||||
F1: FnMut(&WlSurface, &SurfaceData, &T) -> TraversalAction<T>,
|
||||
F2: FnMut(&WlSurface, &SurfaceData, &T),
|
||||
F3: FnMut(&WlSurface, &SurfaceData, &T) -> bool,
|
||||
{
|
||||
PrivateSurfaceData::map_tree(surface, &initial, filter, processor, post_filter, true);
|
||||
}
|
||||
|
||||
/// Check whether this surface as a specific role
|
||||
///
|
||||
/// If the surface is not managed by the `CompositorGlobal` that provided this token, this
|
||||
/// will panic (having more than one compositor is not supported).
|
||||
pub fn has_role<RoleData>(self, surface: &WlSurface) -> bool
|
||||
where
|
||||
R: Role<RoleData>,
|
||||
{
|
||||
SurfaceData::<R>::has_role::<RoleData>(surface)
|
||||
/// Retrieve the parent of this surface
|
||||
///
|
||||
/// Returns `None` is this surface is a root surface
|
||||
pub fn get_parent(surface: &WlSurface) -> Option<WlSurface> {
|
||||
if !surface.as_ref().is_alive() {
|
||||
return None;
|
||||
}
|
||||
PrivateSurfaceData::get_parent(surface)
|
||||
}
|
||||
|
||||
/// Register that this surface has given role with default data
|
||||
///
|
||||
/// Fails if the surface already has a role.
|
||||
///
|
||||
/// If the surface is not managed by the `CompositorGlobal` that provided this token, this
|
||||
/// will panic (having more than one compositor is not supported).
|
||||
pub fn give_role<RoleData>(self, surface: &WlSurface) -> Result<(), AlreadyHasRole>
|
||||
where
|
||||
R: Role<RoleData>,
|
||||
RoleData: Default,
|
||||
{
|
||||
SurfaceData::<R>::give_role::<RoleData>(surface)
|
||||
/// Retrieve the children of this surface
|
||||
pub fn get_children(surface: &WlSurface) -> Vec<WlSurface> {
|
||||
if !surface.as_ref().is_alive() {
|
||||
return Vec::new();
|
||||
}
|
||||
PrivateSurfaceData::get_children(surface)
|
||||
}
|
||||
|
||||
/// Register that this surface has given role with given data
|
||||
///
|
||||
/// Fails if the surface already has a role and returns the data.
|
||||
///
|
||||
/// If the surface is not managed by the `CompositorGlobal` that provided this token, this
|
||||
/// will panic (having more than one compositor is not supported).
|
||||
pub fn give_role_with<RoleData>(self, surface: &WlSurface, data: RoleData) -> Result<(), RoleData>
|
||||
where
|
||||
R: Role<RoleData>,
|
||||
{
|
||||
SurfaceData::<R>::give_role_with::<RoleData>(surface, data)
|
||||
/// Check if this subsurface is a synchronized subsurface
|
||||
///
|
||||
/// Returns false if the surface is already dead
|
||||
pub fn is_sync_subsurface(surface: &WlSurface) -> bool {
|
||||
if !surface.as_ref().is_alive() {
|
||||
return false;
|
||||
}
|
||||
self::handlers::is_effectively_sync(surface)
|
||||
}
|
||||
|
||||
/// Access the role data of a surface
|
||||
///
|
||||
/// Fails and don't call the closure if the surface doesn't have this role
|
||||
///
|
||||
/// If the surface is not managed by the `CompositorGlobal` that provided this token, this
|
||||
/// will panic (having more than one compositor is not supported).
|
||||
pub fn with_role_data<RoleData, F, T>(self, surface: &WlSurface, f: F) -> Result<T, WrongRole>
|
||||
where
|
||||
R: Role<RoleData>,
|
||||
F: FnOnce(&mut RoleData) -> T,
|
||||
{
|
||||
SurfaceData::<R>::with_role_data::<RoleData, _, _>(surface, f)
|
||||
/// Get the current role of this surface
|
||||
pub fn get_role(surface: &WlSurface) -> Option<&'static str> {
|
||||
if !surface.as_ref().is_alive() {
|
||||
return None;
|
||||
}
|
||||
PrivateSurfaceData::get_role(surface)
|
||||
}
|
||||
|
||||
/// Register that this surface does not have a role any longer and retrieve the data
|
||||
///
|
||||
/// Fails if the surface didn't already have this role.
|
||||
///
|
||||
/// If the surface is not managed by the `CompositorGlobal` that provided this token, this
|
||||
/// will panic (having more than one compositor is not supported).
|
||||
pub fn remove_role<RoleData>(self, surface: &WlSurface) -> Result<RoleData, WrongRole>
|
||||
where
|
||||
R: Role<RoleData>,
|
||||
{
|
||||
SurfaceData::<R>::remove_role::<RoleData>(surface)
|
||||
/// Register that this surface has given role
|
||||
///
|
||||
/// Fails if the surface already has a role.
|
||||
pub fn give_role(surface: &WlSurface, role: &'static str) -> Result<(), AlreadyHasRole> {
|
||||
if !surface.as_ref().is_alive() {
|
||||
return Ok(());
|
||||
}
|
||||
PrivateSurfaceData::set_role(surface, role)
|
||||
}
|
||||
|
||||
/// Retrieve the metadata associated with a `wl_region`
|
||||
///
|
||||
/// If the region is not managed by the `CompositorGlobal` that provided this token, this
|
||||
/// will panic (having more than one compositor is not supported).
|
||||
pub fn get_region_attributes(self, region: &wl_region::WlRegion) -> RegionAttributes {
|
||||
/// Access the states associated to this surface
|
||||
pub fn with_states<F, T>(surface: &WlSurface, f: F) -> Result<T, DeadResource>
|
||||
where
|
||||
F: FnOnce(&SurfaceData) -> T,
|
||||
{
|
||||
if !surface.as_ref().is_alive() {
|
||||
return Err(DeadResource);
|
||||
}
|
||||
Ok(PrivateSurfaceData::with_states(surface, f))
|
||||
}
|
||||
|
||||
/// Retrieve the metadata associated with a `wl_region`
|
||||
///
|
||||
/// If the region is not managed by the `CompositorGlobal` that provided this token, this
|
||||
/// will panic (having more than one compositor is not supported).
|
||||
pub fn get_region_attributes(region: &wl_region::WlRegion) -> RegionAttributes {
|
||||
match region.as_ref().user_data().get::<Mutex<RegionAttributes>>() {
|
||||
Some(mutex) => mutex.lock().unwrap().clone(),
|
||||
None => panic!("Accessing the data of foreign regions is not supported."),
|
||||
}
|
||||
}
|
||||
|
||||
/// Register a commit hook to be invoked on surface commit
|
||||
///
|
||||
/// For its precise semantics, see module-level documentation.
|
||||
pub fn add_commit_hook(surface: &WlSurface, hook: fn(&WlSurface)) {
|
||||
if !surface.as_ref().is_alive() {
|
||||
return;
|
||||
}
|
||||
PrivateSurfaceData::add_commit_hook(surface, hook)
|
||||
}
|
||||
|
||||
/// Create new [`wl_compositor`](wayland_server::protocol::wl_compositor)
|
||||
/// and [`wl_subcompositor`](wayland_server::protocol::wl_subcompositor) globals.
|
||||
///
|
||||
/// The globals are directly registered into the event loop, and this function
|
||||
/// returns a [`CompositorToken`] which you'll need access the data associated to
|
||||
/// the [`wl_surface`](wayland_server::protocol::wl_surface)s.
|
||||
///
|
||||
/// It also returns the two global handles, in case you wish to remove these
|
||||
/// globals from the event loop in the future.
|
||||
pub fn compositor_init<R, Impl, L>(
|
||||
/// It returns the two global handles, in case you wish to remove these globals from
|
||||
/// the event loop in the future.
|
||||
pub fn compositor_init<Impl, L>(
|
||||
display: &mut Display,
|
||||
implem: Impl,
|
||||
logger: L,
|
||||
) -> (
|
||||
CompositorToken<R>,
|
||||
Global<wl_compositor::WlCompositor>,
|
||||
Global<wl_subcompositor::WlSubcompositor>,
|
||||
)
|
||||
where
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
R: Default + RoleType + Role<SubsurfaceRole> + Send + 'static,
|
||||
Impl: for<'a> FnMut(SurfaceEvent, WlSurface, CompositorToken<R>, DispatchData<'a>) + 'static,
|
||||
Impl: for<'a> FnMut(WlSurface, DispatchData<'a>) + 'static,
|
||||
{
|
||||
let log = crate::slog_or_fallback(logger).new(o!("smithay_module" => "compositor_handler"));
|
||||
let implem = Rc::new(RefCell::new(implem));
|
||||
|
@ -522,32 +455,18 @@ where
|
|||
let compositor = display.create_global(
|
||||
4,
|
||||
Filter::new(move |(new_compositor, _version), _, _| {
|
||||
self::handlers::implement_compositor::<R, Impl>(new_compositor, log.clone(), implem.clone());
|
||||
self::handlers::implement_compositor::<Impl>(new_compositor, log.clone(), implem.clone());
|
||||
}),
|
||||
);
|
||||
|
||||
let subcompositor = display.create_global(
|
||||
1,
|
||||
Filter::new(move |(new_subcompositor, _version), _, _| {
|
||||
self::handlers::implement_subcompositor::<R>(new_subcompositor);
|
||||
self::handlers::implement_subcompositor(new_subcompositor);
|
||||
}),
|
||||
);
|
||||
|
||||
(CompositorToken::make(), compositor, subcompositor)
|
||||
}
|
||||
|
||||
/// User-handled events for surfaces
|
||||
///
|
||||
/// The global provided by smithay cannot process these events for you, so
|
||||
/// they are forwarded directly via your provided implementation, and are
|
||||
/// described by this global.
|
||||
#[derive(Debug)]
|
||||
pub enum SurfaceEvent {
|
||||
/// The double-buffered state has been validated by the client
|
||||
///
|
||||
/// At this point, the pending state that has been accumulated in the [`SurfaceAttributes`] associated
|
||||
/// to this surface should be atomically integrated into the current state of the surface.
|
||||
Commit,
|
||||
(compositor, subcompositor)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -1,280 +0,0 @@
|
|||
//! Tools for handling surface roles
|
||||
//!
|
||||
//! In the Wayland protocol, surfaces can have several different roles, which
|
||||
//! define how they are to be used. The core protocol defines 3 of these roles:
|
||||
//!
|
||||
//! - `shell_surface`: This surface is to be considered as what is most often
|
||||
//! called a "window".
|
||||
//! - `pointer_surface`: This surface represent the contents of a pointer icon
|
||||
//! and replaces the default pointer.
|
||||
//! - `subsurface`: This surface is part of a subsurface tree, and as such has
|
||||
//! a parent surface.
|
||||
//!
|
||||
//! A surface can have only one role at any given time. To change he role of a
|
||||
//! surface, the client must first remove the previous role before assigning the
|
||||
//! new one. A surface without a role is not displayed at all.
|
||||
//!
|
||||
//! This module provides tools to manage roles of a surface in a composable way
|
||||
//! allowing all handlers of smithay to manage surface roles while being aware
|
||||
//! of the possible role conflicts.
|
||||
//!
|
||||
//! ## General mechanism
|
||||
//!
|
||||
//! First, all roles need to have an unique type, holding its metadata and identifying it
|
||||
//! to the type-system. Even if your role does not hold any metadata, you still need its
|
||||
//! unique type, using a unit-like struct rather than `()`.
|
||||
//!
|
||||
//! You then need a type for managing the roles of a surface. This type holds information
|
||||
//! about what is the current role of a surface, and what is the metadata associated with
|
||||
//! it.
|
||||
//!
|
||||
//! For convenience, you can use the `define_roles!` macro provided by Smithay to define this
|
||||
//! type. You can call it like this:
|
||||
//!
|
||||
//! ```
|
||||
//! # use smithay::define_roles;
|
||||
//! // Metadata for a first role
|
||||
//! #[derive(Default)]
|
||||
//! pub struct MyRoleMetadata {
|
||||
//! }
|
||||
//!
|
||||
//! // Metadata for a second role
|
||||
//! #[derive(Default)]
|
||||
//! pub struct MyRoleMetadata2 {
|
||||
//! }
|
||||
//!
|
||||
//! define_roles!(Roles =>
|
||||
//! // You can put several roles like this
|
||||
//! // first identifier is the name of the variant for this
|
||||
//! // role in the generated enum, second is the token type
|
||||
//! // for this role
|
||||
//! [MyRoleName, MyRoleMetadata]
|
||||
//! [MyRoleName2, MyRoleMetadata2]
|
||||
//! /* ... */
|
||||
//! );
|
||||
//!
|
||||
//! ```
|
||||
//!
|
||||
//! And this will expand to an enum like this:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! pub enum Roles {
|
||||
//! NoRole,
|
||||
//! // The subsurface role is always inserted, as it is required
|
||||
//! // by the CompositorHandler
|
||||
//! Subsurface(::smithay::compositor::SubsurfaceAttributes),
|
||||
//! // all your other roles come here
|
||||
//! MyRoleName(MyRoleMetadata),
|
||||
//! MyRoleName2(MyRoleMetadata2),
|
||||
//! /* ... */
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! as well as implement a few trait for it, allowing it to be used by
|
||||
//! all smithay handlers:
|
||||
//!
|
||||
//! - The trait [`RoleType`](RoleType),
|
||||
//! which defines it as a type handling roles
|
||||
//! - For each of your roles, the trait [`Role<Token>`](Role)
|
||||
//! (where `Token` is your token type), marking its ability to handle this given role.
|
||||
//!
|
||||
//! All handlers that handle a specific role will require you to provide
|
||||
//! them with a [`CompositorToken<U, R, H>`](crate::wayland::compositor::CompositorToken)
|
||||
//! where `R: Role<TheToken>`.
|
||||
//!
|
||||
//! See the documentation of these traits for their specific definition and
|
||||
//! capabilities.
|
||||
|
||||
/// An error type signifying that the surface does not have expected role
|
||||
///
|
||||
/// Generated if you attempt a role operation on a surface that does
|
||||
/// not have the role you asked for.
|
||||
#[derive(Debug)]
|
||||
pub struct WrongRole;
|
||||
|
||||
impl std::fmt::Display for WrongRole {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("Wrong role for surface.")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for WrongRole {}
|
||||
|
||||
/// An error type signifying that the surface already has a role and
|
||||
/// cannot be assigned an other
|
||||
///
|
||||
/// Generated if you attempt a role operation on a surface that does
|
||||
/// not have the role you asked for.
|
||||
#[derive(Debug)]
|
||||
pub struct AlreadyHasRole;
|
||||
|
||||
impl std::fmt::Display for AlreadyHasRole {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("Surface already has a role.")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for AlreadyHasRole {}
|
||||
|
||||
/// A trait representing a type that can manage surface roles
|
||||
pub trait RoleType {
|
||||
/// Check if the associated surface has a role
|
||||
///
|
||||
/// Only reports if the surface has any role or no role.
|
||||
/// To check for a role in particular, see [`Role::has`].
|
||||
fn has_role(&self) -> bool;
|
||||
}
|
||||
|
||||
/// A trait representing the capability of a [`RoleType`] to handle a given role
|
||||
///
|
||||
/// This trait allows to interact with the different roles a [`RoleType`] can
|
||||
/// handle.
|
||||
///
|
||||
/// This trait is meant to be used generically, for example, to retrieve the
|
||||
/// data associated with a given role with token `TheRole`:
|
||||
///
|
||||
/// ```ignore
|
||||
/// let data = <MyRoles as Role<RoleData>>::data(my_roles)
|
||||
/// .expect("The surface does not have this role.");
|
||||
/// ```
|
||||
///
|
||||
/// The methods of this trait are mirrored on
|
||||
/// [`CompositorToken`](crate::wayland::compositor::CompositorToken) for easy
|
||||
/// access to the role data of the surfaces.
|
||||
///
|
||||
/// Note that if a role is automatically handled for you by a Handler provided
|
||||
/// by smithay, you should not set or unset it manually on a surface. Doing so
|
||||
/// would likely corrupt the internal state of these handlers, causing spurious
|
||||
/// protocol errors and unreliable behaviour overall.
|
||||
pub trait Role<R>: RoleType {
|
||||
/// Set the role for the associated surface with default associated data
|
||||
///
|
||||
/// Fails if the surface already has a role
|
||||
fn set(&mut self) -> Result<(), AlreadyHasRole>
|
||||
where
|
||||
R: Default,
|
||||
{
|
||||
self.set_with(Default::default()).map_err(|_| AlreadyHasRole)
|
||||
}
|
||||
|
||||
/// Set the role for the associated surface with given data
|
||||
///
|
||||
/// Fails if the surface already has a role and returns the data
|
||||
fn set_with(&mut self, data: R) -> Result<(), R>;
|
||||
|
||||
/// Check if the associated surface has this role
|
||||
fn has(&self) -> bool;
|
||||
|
||||
/// Access the data associated with this role if its the current one
|
||||
fn data(&self) -> Result<&R, WrongRole>;
|
||||
|
||||
/// Mutably access the data associated with this role if its the current one
|
||||
fn data_mut(&mut self) -> Result<&mut R, WrongRole>;
|
||||
|
||||
/// Remove this role from the associated surface
|
||||
///
|
||||
/// Fails if the surface does not currently have this role
|
||||
fn unset(&mut self) -> Result<R, WrongRole>;
|
||||
}
|
||||
|
||||
/// The roles defining macro
|
||||
///
|
||||
/// See the docs of the [`wayland::compositor::roles`](wayland/compositor/roles/index.html) module
|
||||
/// for an explanation of its use.
|
||||
#[macro_export]
|
||||
macro_rules! define_roles(
|
||||
($enum_name: ident) => {
|
||||
define_roles!($enum_name =>);
|
||||
};
|
||||
($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_attr])* [$role_name, $role_data])*
|
||||
);
|
||||
};
|
||||
(__impl $enum_name:ident => $($(#[$role_attr:meta])* [$role_name: ident, $role_data: ty])*) => {
|
||||
pub enum $enum_name {
|
||||
NoRole,
|
||||
$($(#[$role_attr])* $role_name($role_data)),*
|
||||
}
|
||||
|
||||
impl Default for $enum_name {
|
||||
fn default() -> $enum_name {
|
||||
$enum_name::NoRole
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::wayland::compositor::roles::RoleType for $enum_name {
|
||||
fn has_role(&self) -> bool {
|
||||
if let $enum_name::NoRole = *self {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
$(#[$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 {
|
||||
*self = $enum_name::$role_name(data);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(data)
|
||||
}
|
||||
}
|
||||
|
||||
fn has(&self) -> bool {
|
||||
if let $enum_name::$role_name(_) = *self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn data(&self) -> ::std::result::Result<
|
||||
&$role_data,
|
||||
$crate::wayland::compositor::roles::WrongRole
|
||||
>
|
||||
{
|
||||
if let $enum_name::$role_name(ref data) = *self {
|
||||
Ok(data)
|
||||
} else {
|
||||
Err($crate::wayland::compositor::roles::WrongRole)
|
||||
}
|
||||
}
|
||||
|
||||
fn data_mut(&mut self) -> ::std::result::Result<
|
||||
&mut $role_data,
|
||||
$crate::wayland::compositor::roles::WrongRole
|
||||
>
|
||||
{
|
||||
if let $enum_name::$role_name(ref mut data) = *self {
|
||||
Ok(data)
|
||||
} else {
|
||||
Err($crate::wayland::compositor::roles::WrongRole)
|
||||
}
|
||||
}
|
||||
|
||||
fn unset(&mut self) -> ::std::result::Result<
|
||||
$role_data,
|
||||
$crate::wayland::compositor::roles::WrongRole
|
||||
>
|
||||
{
|
||||
// remove self to make borrow checker happy
|
||||
let temp = ::std::mem::replace(self, $enum_name::NoRole);
|
||||
if let $enum_name::$role_name(data) = temp {
|
||||
Ok(data)
|
||||
} else {
|
||||
// put it back in place
|
||||
*self = temp;
|
||||
Err($crate::wayland::compositor::roles::WrongRole)
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
);
|
|
@ -0,0 +1,288 @@
|
|||
// The transaction model for handling surface states in Smithay
|
||||
//
|
||||
// The caching logic in `cache.rs` provides surfaces with a queue of
|
||||
// pending states identified with numeric commit ids, allowing the compositor
|
||||
// to precisely control *when* a state become active. This file is the second
|
||||
// half: these identified states are grouped into transactions, which allow the
|
||||
// synchronization of updates accross surfaces.
|
||||
//
|
||||
// There are 2 main cases when the state of multiple surfaces must be updated
|
||||
// atomically:
|
||||
// - synchronized subsurface must have their state updated at the same time as their parents
|
||||
// - The upcoming `wp_transaction` protocol
|
||||
//
|
||||
// In these situations, the individual states in a surface queue are grouped into a transaction
|
||||
// and are all applied atomically when the transaction itself is applied. The logic for creating
|
||||
// new transactions is currently the following:
|
||||
//
|
||||
// - Each surface has an implicit "pending" transaction, into which its newly commited state is
|
||||
// recorded
|
||||
// - Furthermore, on commit, the pending transaction of all synchronized child subsurfaces is merged
|
||||
// into the current surface's pending transaction, and a new implicit transaction is started for those
|
||||
// children (logic is implemented in `handlers.rs`, in `PrivateSurfaceData::commit`).
|
||||
// - Then, still on commit, if the surface is not a synchronized subsurface, its pending transaction is
|
||||
// directly applied
|
||||
//
|
||||
// This last step will change once we have support for explicit synchronization (and further in the future,
|
||||
// of the wp_transaction protocol). Explicit synchronization introduces a notion of blockers: the transaction
|
||||
// cannot be applied before all blockers are released, and thus must wait for it to be the case.
|
||||
//
|
||||
// For thoses situations, the (currently unused) `TransactionQueue` will come into play. It is a per-client
|
||||
// queue of transactions, that stores and applies them by both respecting their topological order
|
||||
// (ensuring that for each surface, states are applied in the correct order) and that all transactions
|
||||
// wait befor all their blockers are resolved to be merged. If a blocker is cancelled, the whole transaction
|
||||
// it blocks is cancelled as well, and simply dropped. Thanks to the logic of `Cache::apply_state`, the
|
||||
// associated state will be applied automatically when the next valid transaction is applied, ensuring
|
||||
// global coherence.
|
||||
|
||||
// A significant part of the logic of this module is not yet used,
|
||||
// but will be once proper transaction & blockers support is
|
||||
// added to smithay
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use wayland_server::protocol::wl_surface::WlSurface;
|
||||
|
||||
use crate::wayland::Serial;
|
||||
|
||||
use super::tree::PrivateSurfaceData;
|
||||
|
||||
pub trait Blocker {
|
||||
fn state(&self) -> BlockerState;
|
||||
}
|
||||
|
||||
pub enum BlockerState {
|
||||
Pending,
|
||||
Released,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
struct TransactionState {
|
||||
surfaces: Vec<(WlSurface, Serial)>,
|
||||
blockers: Vec<Box<dyn Blocker + Send>>,
|
||||
}
|
||||
|
||||
impl Default for TransactionState {
|
||||
fn default() -> Self {
|
||||
TransactionState {
|
||||
surfaces: Vec::new(),
|
||||
blockers: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactionState {
|
||||
fn insert(&mut self, surface: WlSurface, id: Serial) {
|
||||
if let Some(place) = self.surfaces.iter_mut().find(|place| place.0 == surface) {
|
||||
// the surface is already in the list, update the serial
|
||||
if place.1 < id {
|
||||
place.1 = id;
|
||||
}
|
||||
} else {
|
||||
// the surface is not in the list, insert it
|
||||
self.surfaces.push((surface, id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum TransactionInner {
|
||||
Data(TransactionState),
|
||||
Fused(Arc<Mutex<TransactionInner>>),
|
||||
}
|
||||
|
||||
pub(crate) struct PendingTransaction {
|
||||
inner: Arc<Mutex<TransactionInner>>,
|
||||
}
|
||||
|
||||
impl Default for PendingTransaction {
|
||||
fn default() -> Self {
|
||||
PendingTransaction {
|
||||
inner: Arc::new(Mutex::new(TransactionInner::Data(Default::default()))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PendingTransaction {
|
||||
fn with_inner_state<T, F: FnOnce(&mut TransactionState) -> T>(&self, f: F) -> T {
|
||||
let mut next = self.inner.clone();
|
||||
loop {
|
||||
let tmp = match *next.lock().unwrap() {
|
||||
TransactionInner::Data(ref mut state) => return f(state),
|
||||
TransactionInner::Fused(ref into) => into.clone(),
|
||||
};
|
||||
next = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn insert_state(&self, surface: WlSurface, id: Serial) {
|
||||
self.with_inner_state(|state| state.insert(surface, id))
|
||||
}
|
||||
|
||||
pub(crate) fn add_blocker<B: Blocker + Send + 'static>(&self, blocker: B) {
|
||||
self.with_inner_state(|state| state.blockers.push(Box::new(blocker) as Box<_>))
|
||||
}
|
||||
|
||||
pub(crate) fn is_same_as(&self, other: &PendingTransaction) -> bool {
|
||||
let ptr1 = self.with_inner_state(|state| state as *const _);
|
||||
let ptr2 = other.with_inner_state(|state| state as *const _);
|
||||
ptr1 == ptr2
|
||||
}
|
||||
|
||||
pub(crate) fn merge_into(&self, into: &PendingTransaction) {
|
||||
if self.is_same_as(into) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
// extract our pending surfaces and change our link
|
||||
let mut next = self.inner.clone();
|
||||
let my_state;
|
||||
loop {
|
||||
let tmp = {
|
||||
let mut guard = next.lock().unwrap();
|
||||
match *guard {
|
||||
TransactionInner::Data(ref mut state) => {
|
||||
my_state = std::mem::take(state);
|
||||
*guard = TransactionInner::Fused(into.inner.clone());
|
||||
break;
|
||||
}
|
||||
TransactionInner::Fused(ref into) => into.clone(),
|
||||
}
|
||||
};
|
||||
next = tmp;
|
||||
}
|
||||
// fuse our surfaces into our new transaction state
|
||||
self.with_inner_state(|state| {
|
||||
for (surface, id) in my_state.surfaces {
|
||||
state.insert(surface, id);
|
||||
}
|
||||
state.blockers.extend(my_state.blockers);
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn finalize(mut self) -> Transaction {
|
||||
// When finalizing a transaction, this *must* be the last handle to this transaction
|
||||
loop {
|
||||
let inner = match Arc::try_unwrap(self.inner) {
|
||||
Ok(mutex) => mutex.into_inner().unwrap(),
|
||||
Err(_) => panic!("Attempting to finalize a transaction but handle is not the last."),
|
||||
};
|
||||
match inner {
|
||||
TransactionInner::Data(TransactionState {
|
||||
surfaces, blockers, ..
|
||||
}) => return Transaction { surfaces, blockers },
|
||||
TransactionInner::Fused(into) => self.inner = into,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(crate) struct Transaction {
|
||||
surfaces: Vec<(WlSurface, Serial)>,
|
||||
blockers: Vec<Box<dyn Blocker + Send>>,
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
/// Computes the global state of the transaction wrt its blockers
|
||||
///
|
||||
/// The logic is:
|
||||
///
|
||||
/// - if at least one blocker is cancelled, the transaction is cancelled
|
||||
/// - otherwise, if at least one blocker is pending, the transaction is pending
|
||||
/// - otherwise, all blockers are released, and the transaction is also released
|
||||
pub(crate) fn state(&self) -> BlockerState {
|
||||
use BlockerState::*;
|
||||
self.blockers
|
||||
.iter()
|
||||
.fold(Released, |acc, blocker| match (acc, blocker.state()) {
|
||||
(Cancelled, _) | (_, Cancelled) => Cancelled,
|
||||
(Pending, _) | (_, Pending) => Pending,
|
||||
(Released, Released) => Released,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn apply(self) {
|
||||
for (surface, id) in self.surfaces {
|
||||
PrivateSurfaceData::with_states(&surface, |states| {
|
||||
states.cached_state.apply_state(id);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This queue should be per-client
|
||||
pub(crate) struct TransactionQueue {
|
||||
transactions: Vec<Transaction>,
|
||||
// we keep the hashset around to reuse allocations
|
||||
seen_surfaces: HashSet<u32>,
|
||||
}
|
||||
|
||||
impl Default for TransactionQueue {
|
||||
fn default() -> Self {
|
||||
TransactionQueue {
|
||||
transactions: Vec::new(),
|
||||
seen_surfaces: HashSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactionQueue {
|
||||
pub(crate) fn append(&mut self, t: Transaction) {
|
||||
self.transactions.push(t);
|
||||
}
|
||||
|
||||
pub(crate) fn apply_ready(&mut self) {
|
||||
// this is a very non-optimized implementation
|
||||
// we just iterate over the queue of transactions, keeping track of which
|
||||
// surface we have seen as they encode transaction dependencies
|
||||
self.seen_surfaces.clear();
|
||||
// manually iterate as we're going to modify the Vec while iterating on it
|
||||
let mut i = 0;
|
||||
// the loop will terminate, as at every iteration either i is incremented by 1
|
||||
// or the lenght of self.transactions is reduced by 1.
|
||||
while i <= self.transactions.len() {
|
||||
let mut skip = false;
|
||||
// does the transaction have any active blocker?
|
||||
match self.transactions[i].state() {
|
||||
BlockerState::Cancelled => {
|
||||
// this transaction is cancelled, remove it without further processing
|
||||
self.transactions.remove(i);
|
||||
continue;
|
||||
}
|
||||
BlockerState::Pending => {
|
||||
skip = true;
|
||||
}
|
||||
BlockerState::Released => {}
|
||||
}
|
||||
// if not, does this transaction depend on any previous transaction?
|
||||
if !skip {
|
||||
for (s, _) in &self.transactions[i].surfaces {
|
||||
if !s.as_ref().is_alive() {
|
||||
continue;
|
||||
}
|
||||
if self.seen_surfaces.contains(&s.as_ref().id()) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if skip {
|
||||
// this transaction is not yet ready and should be skipped, add its surfaces to our
|
||||
// seen list
|
||||
for (s, _) in &self.transactions[i].surfaces {
|
||||
if !s.as_ref().is_alive() {
|
||||
continue;
|
||||
}
|
||||
self.seen_surfaces.insert(s.as_ref().id());
|
||||
}
|
||||
i += 1;
|
||||
} else {
|
||||
// this transaction is to be applied, yay!
|
||||
self.transactions.remove(i).apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,14 @@
|
|||
use super::{roles::*, SubsurfaceRole, SurfaceAttributes};
|
||||
use std::sync::Mutex;
|
||||
use crate::wayland::Serial;
|
||||
|
||||
use super::{
|
||||
cache::MultiCache, get_children, handlers::is_effectively_sync, transaction::PendingTransaction,
|
||||
SurfaceData,
|
||||
};
|
||||
use std::sync::{atomic::Ordering, Mutex};
|
||||
use wayland_server::protocol::wl_surface::WlSurface;
|
||||
|
||||
pub(crate) static SUBSURFACE_ROLE: &str = "subsurface";
|
||||
|
||||
/// Node of a subsurface tree, holding some user specified data type U
|
||||
/// at each node
|
||||
///
|
||||
|
@ -15,13 +22,31 @@ use wayland_server::protocol::wl_surface::WlSurface;
|
|||
///
|
||||
/// Each node also appears within its children list, to allow relative placement
|
||||
/// between them.
|
||||
pub struct SurfaceData<R> {
|
||||
pub struct PrivateSurfaceData {
|
||||
parent: Option<WlSurface>,
|
||||
children: Vec<WlSurface>,
|
||||
role: R,
|
||||
attributes: SurfaceAttributes,
|
||||
public_data: SurfaceData,
|
||||
pending_transaction: PendingTransaction,
|
||||
current_txid: Serial,
|
||||
commit_hooks: Vec<fn(&WlSurface)>,
|
||||
}
|
||||
|
||||
/// An error type signifying that the surface already has a role and
|
||||
/// cannot be assigned an other
|
||||
///
|
||||
/// Generated if you attempt a role operation on a surface that does
|
||||
/// not have the role you asked for.
|
||||
#[derive(Debug)]
|
||||
pub struct AlreadyHasRole;
|
||||
|
||||
impl std::fmt::Display for AlreadyHasRole {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("Surface already has a role.")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for AlreadyHasRole {}
|
||||
|
||||
pub enum Location {
|
||||
Before,
|
||||
After,
|
||||
|
@ -38,27 +63,28 @@ pub enum TraversalAction<T> {
|
|||
Break,
|
||||
}
|
||||
|
||||
impl<R: Default> SurfaceData<R> {
|
||||
pub fn new() -> Mutex<SurfaceData<R>> {
|
||||
Mutex::new(SurfaceData {
|
||||
impl PrivateSurfaceData {
|
||||
pub fn new() -> Mutex<PrivateSurfaceData> {
|
||||
Mutex::new(PrivateSurfaceData {
|
||||
parent: None,
|
||||
children: vec![],
|
||||
public_data: SurfaceData {
|
||||
role: Default::default(),
|
||||
attributes: Default::default(),
|
||||
data_map: Default::default(),
|
||||
cached_state: MultiCache::new(),
|
||||
},
|
||||
pending_transaction: Default::default(),
|
||||
current_txid: Serial(0),
|
||||
commit_hooks: Vec::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> SurfaceData<R>
|
||||
where
|
||||
R: 'static,
|
||||
{
|
||||
/// Initializes the surface, must be called at creation for state coherence
|
||||
pub fn init(surface: &WlSurface) {
|
||||
let my_data_mutex = surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceData<R>>>()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let mut my_data = my_data_mutex.lock().unwrap();
|
||||
debug_assert!(my_data.children.is_empty());
|
||||
|
@ -70,7 +96,7 @@ where
|
|||
let my_data_mutex = surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceData<R>>>()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let mut my_data = my_data_mutex.lock().unwrap();
|
||||
if let Some(old_parent) = my_data.parent.take() {
|
||||
|
@ -78,7 +104,7 @@ where
|
|||
let old_parent_mutex = old_parent
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceData<R>>>()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let mut old_parent_guard = old_parent_mutex.lock().unwrap();
|
||||
old_parent_guard
|
||||
|
@ -87,7 +113,11 @@ where
|
|||
}
|
||||
// orphan all our children
|
||||
for child in &my_data.children {
|
||||
let child_mutex = child.as_ref().user_data().get::<Mutex<SurfaceData<R>>>().unwrap();
|
||||
let child_mutex = child
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
if std::ptr::eq(child_mutex, my_data_mutex) {
|
||||
// This child is ourselves, don't do anything.
|
||||
continue;
|
||||
|
@ -97,108 +127,119 @@ where
|
|||
child_guard.parent = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: RoleType + 'static> SurfaceData<R> {
|
||||
pub fn has_a_role(surface: &WlSurface) -> bool {
|
||||
debug_assert!(surface.as_ref().is_alive());
|
||||
let data_mutex = surface
|
||||
pub fn set_role(surface: &WlSurface, role: &'static str) -> Result<(), AlreadyHasRole> {
|
||||
let my_data_mutex = surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceData<R>>>()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let data_guard = data_mutex.lock().unwrap();
|
||||
<R as RoleType>::has_role(&data_guard.role)
|
||||
let mut my_data = my_data_mutex.lock().unwrap();
|
||||
if my_data.public_data.role.is_some() {
|
||||
return Err(AlreadyHasRole);
|
||||
}
|
||||
my_data.public_data.role = Some(role);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check whether a surface has a given role
|
||||
pub fn has_role<RoleData>(surface: &WlSurface) -> bool
|
||||
where
|
||||
R: Role<RoleData>,
|
||||
{
|
||||
debug_assert!(surface.as_ref().is_alive());
|
||||
let data_mutex = surface
|
||||
pub fn get_role(surface: &WlSurface) -> Option<&'static str> {
|
||||
let my_data_mutex = surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceData<R>>>()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let data_guard = data_mutex.lock().unwrap();
|
||||
<R as Role<RoleData>>::has(&data_guard.role)
|
||||
let my_data = my_data_mutex.lock().unwrap();
|
||||
my_data.public_data.role
|
||||
}
|
||||
|
||||
/// Register that this surface has a role, fails if it already has one
|
||||
pub fn give_role<RoleData>(surface: &WlSurface) -> Result<(), AlreadyHasRole>
|
||||
where
|
||||
R: Role<RoleData>,
|
||||
RoleData: Default,
|
||||
{
|
||||
debug_assert!(surface.as_ref().is_alive());
|
||||
let data_mutex = surface
|
||||
pub fn with_states<T, F: FnOnce(&SurfaceData) -> T>(surface: &WlSurface, f: F) -> T {
|
||||
let my_data_mutex = surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceData<R>>>()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let mut data_guard = data_mutex.lock().unwrap();
|
||||
<R as Role<RoleData>>::set(&mut data_guard.role)
|
||||
let my_data = my_data_mutex.lock().unwrap();
|
||||
f(&my_data.public_data)
|
||||
}
|
||||
|
||||
/// Register that this surface has a role with given data
|
||||
///
|
||||
/// Fails if it already has one and returns the data
|
||||
pub fn give_role_with<RoleData>(surface: &WlSurface, data: RoleData) -> Result<(), RoleData>
|
||||
where
|
||||
R: Role<RoleData>,
|
||||
{
|
||||
debug_assert!(surface.as_ref().is_alive());
|
||||
let data_mutex = surface
|
||||
pub fn add_commit_hook(surface: &WlSurface, hook: fn(&WlSurface)) {
|
||||
let my_data_mutex = surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceData<R>>>()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let mut data_guard = data_mutex.lock().unwrap();
|
||||
<R as Role<RoleData>>::set_with(&mut data_guard.role, data)
|
||||
let mut my_data = my_data_mutex.lock().unwrap();
|
||||
my_data.commit_hooks.push(hook);
|
||||
}
|
||||
|
||||
/// Register that this surface has no role and returns the data
|
||||
///
|
||||
/// It is a noop if this surface already didn't have one, but fails if
|
||||
/// the role was "subsurface", it must be removed by the `unset_parent` method.
|
||||
pub fn remove_role<RoleData>(surface: &WlSurface) -> Result<RoleData, WrongRole>
|
||||
where
|
||||
R: Role<RoleData>,
|
||||
{
|
||||
debug_assert!(surface.as_ref().is_alive());
|
||||
let data_mutex = surface
|
||||
pub fn invoke_commit_hooks(surface: &WlSurface) {
|
||||
// don't hold the mutex while the hooks are invoked
|
||||
let hooks = {
|
||||
let my_data_mutex = surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceData<R>>>()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let mut data_guard = data_mutex.lock().unwrap();
|
||||
<R as Role<RoleData>>::unset(&mut data_guard.role)
|
||||
let my_data = my_data_mutex.lock().unwrap();
|
||||
my_data.commit_hooks.clone()
|
||||
};
|
||||
for hook in hooks {
|
||||
hook(surface);
|
||||
}
|
||||
}
|
||||
|
||||
/// Access to the role data
|
||||
pub fn with_role_data<RoleData, F, T>(surface: &WlSurface, f: F) -> Result<T, WrongRole>
|
||||
where
|
||||
R: Role<RoleData>,
|
||||
F: FnOnce(&mut RoleData) -> T,
|
||||
{
|
||||
debug_assert!(surface.as_ref().is_alive());
|
||||
let data_mutex = surface
|
||||
pub fn commit(surface: &WlSurface) {
|
||||
let is_sync = is_effectively_sync(surface);
|
||||
let children = get_children(surface);
|
||||
let my_data_mutex = surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceData<R>>>()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let mut data_guard = data_mutex.lock().unwrap();
|
||||
let data = <R as Role<RoleData>>::data_mut(&mut data_guard.role)?;
|
||||
Ok(f(data))
|
||||
let mut my_data = my_data_mutex.lock().unwrap();
|
||||
// commit our state
|
||||
let current_txid = my_data.current_txid;
|
||||
my_data.public_data.cached_state.commit(Some(current_txid));
|
||||
// take all our children state into our pending transaction
|
||||
for child in children {
|
||||
let child_data_mutex = child
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
// if the child is effectively sync, take its state
|
||||
// this is the case if either we are effectively sync, or the child is explicitly sync
|
||||
let mut child_data = child_data_mutex.lock().unwrap();
|
||||
let is_child_sync = || {
|
||||
child_data
|
||||
.public_data
|
||||
.data_map
|
||||
.get::<super::handlers::SubsurfaceState>()
|
||||
.map(|s| s.sync.load(Ordering::Acquire))
|
||||
.unwrap_or(false)
|
||||
};
|
||||
if is_sync || is_child_sync() {
|
||||
let child_tx = std::mem::take(&mut child_data.pending_transaction);
|
||||
child_tx.merge_into(&my_data.pending_transaction);
|
||||
child_data.current_txid.0 = child_data.current_txid.0.wrapping_add(1);
|
||||
}
|
||||
}
|
||||
my_data
|
||||
.pending_transaction
|
||||
.insert_state(surface.clone(), my_data.current_txid);
|
||||
if !is_sync {
|
||||
// if we are not sync, apply the transaction immediately
|
||||
let tx = std::mem::take(&mut my_data.pending_transaction);
|
||||
// release the mutex, as applying the transaction will try to lock it
|
||||
std::mem::drop(my_data);
|
||||
// apply the transaction
|
||||
tx.finalize().apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<R> {
|
||||
/// Checks if the first surface is an ancestor of the second
|
||||
pub fn is_ancestor(a: &WlSurface, b: &WlSurface) -> bool {
|
||||
let b_mutex = b.as_ref().user_data().get::<Mutex<SurfaceData<R>>>().unwrap();
|
||||
let b_mutex = b.as_ref().user_data().get::<Mutex<PrivateSurfaceData>>().unwrap();
|
||||
let b_guard = b_mutex.lock().unwrap();
|
||||
if let Some(ref parent) = b_guard.parent {
|
||||
if parent.as_ref().equals(a.as_ref()) {
|
||||
|
@ -225,10 +266,18 @@ impl<R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<R> {
|
|||
|
||||
// change child's parent
|
||||
{
|
||||
let child_mutex = child.as_ref().user_data().get::<Mutex<SurfaceData<R>>>().unwrap();
|
||||
let child_mutex = child
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let mut child_guard = child_mutex.lock().unwrap();
|
||||
// if surface already has a role, it cannot become a subsurface
|
||||
<R as Role<SubsurfaceRole>>::set(&mut child_guard.role)?;
|
||||
if child_guard.public_data.role.is_some() && child_guard.public_data.role != Some(SUBSURFACE_ROLE)
|
||||
{
|
||||
return Err(AlreadyHasRole);
|
||||
}
|
||||
child_guard.public_data.role = Some(SUBSURFACE_ROLE);
|
||||
debug_assert!(child_guard.parent.is_none());
|
||||
child_guard.parent = Some(parent.clone());
|
||||
}
|
||||
|
@ -237,7 +286,7 @@ impl<R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<R> {
|
|||
let parent_mutex = parent
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceData<R>>>()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let mut parent_guard = parent_mutex.lock().unwrap();
|
||||
parent_guard.children.push(child.clone())
|
||||
|
@ -251,22 +300,20 @@ impl<R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<R> {
|
|||
pub fn unset_parent(child: &WlSurface) {
|
||||
debug_assert!(child.as_ref().is_alive());
|
||||
let old_parent = {
|
||||
let child_mutex = child.as_ref().user_data().get::<Mutex<SurfaceData<R>>>().unwrap();
|
||||
let child_mutex = child
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let mut child_guard = child_mutex.lock().unwrap();
|
||||
let old_parent = child_guard.parent.take();
|
||||
if old_parent.is_some() {
|
||||
// We had a parent, so this does not have a role any more
|
||||
<R as Role<SubsurfaceRole>>::unset(&mut child_guard.role)
|
||||
.expect("Surface had a parent but not the subsurface role?!");
|
||||
}
|
||||
old_parent
|
||||
child_guard.parent.take()
|
||||
};
|
||||
// unregister from our parent
|
||||
if let Some(old_parent) = old_parent {
|
||||
let parent_mutex = old_parent
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceData<R>>>()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let mut parent_guard = parent_mutex.lock().unwrap();
|
||||
parent_guard
|
||||
|
@ -277,7 +324,11 @@ impl<R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<R> {
|
|||
|
||||
/// Retrieve the parent surface (if any) of this surface
|
||||
pub fn get_parent(child: &WlSurface) -> Option<WlSurface> {
|
||||
let child_mutex = child.as_ref().user_data().get::<Mutex<SurfaceData<R>>>().unwrap();
|
||||
let child_mutex = child
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let child_guard = child_mutex.lock().unwrap();
|
||||
child_guard.parent.as_ref().cloned()
|
||||
}
|
||||
|
@ -287,7 +338,7 @@ impl<R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<R> {
|
|||
let parent_mutex = parent
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceData<R>>>()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let parent_guard = parent_mutex.lock().unwrap();
|
||||
parent_guard
|
||||
|
@ -306,7 +357,7 @@ impl<R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<R> {
|
|||
let data_mutex = surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceData<R>>>()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let data_guard = data_mutex.lock().unwrap();
|
||||
data_guard.parent.as_ref().cloned().unwrap()
|
||||
|
@ -324,7 +375,7 @@ impl<R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<R> {
|
|||
let parent_mutex = parent
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceData<R>>>()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let mut parent_guard = parent_mutex.lock().unwrap();
|
||||
let my_index = index_of(surface, &parent_guard.children).unwrap();
|
||||
|
@ -345,24 +396,7 @@ impl<R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<R> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: 'static> SurfaceData<R> {
|
||||
/// Access the attributes associated with a surface
|
||||
///
|
||||
/// Note that an internal lock is taken during access of this data,
|
||||
/// so the tree cannot be manipulated at the same time
|
||||
pub fn with_data<T, F>(surface: &WlSurface, f: F) -> T
|
||||
where
|
||||
F: FnOnce(&mut SurfaceAttributes) -> T,
|
||||
{
|
||||
let data_mutex = surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceData<R>>>()
|
||||
.expect("Accessing the data of foreign surfaces is not supported.");
|
||||
let mut data_guard = data_mutex.lock().unwrap();
|
||||
f(&mut data_guard.attributes)
|
||||
}
|
||||
|
||||
impl PrivateSurfaceData {
|
||||
/// Access sequentially the attributes associated with a surface tree,
|
||||
/// in a depth-first order.
|
||||
///
|
||||
|
@ -384,9 +418,9 @@ impl<R: 'static> SurfaceData<R> {
|
|||
mut post_filter: F3,
|
||||
reverse: bool,
|
||||
) where
|
||||
F1: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction<T>,
|
||||
F2: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T),
|
||||
F3: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T) -> bool,
|
||||
F1: FnMut(&WlSurface, &SurfaceData, &T) -> TraversalAction<T>,
|
||||
F2: FnMut(&WlSurface, &SurfaceData, &T),
|
||||
F3: FnMut(&WlSurface, &SurfaceData, &T) -> bool,
|
||||
{
|
||||
Self::map(
|
||||
surface,
|
||||
|
@ -408,25 +442,25 @@ impl<R: 'static> SurfaceData<R> {
|
|||
reverse: bool,
|
||||
) -> bool
|
||||
where
|
||||
F1: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction<T>,
|
||||
F2: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T),
|
||||
F3: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T) -> bool,
|
||||
F1: FnMut(&WlSurface, &SurfaceData, &T) -> TraversalAction<T>,
|
||||
F2: FnMut(&WlSurface, &SurfaceData, &T),
|
||||
F3: FnMut(&WlSurface, &SurfaceData, &T) -> bool,
|
||||
{
|
||||
let data_mutex = surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceData<R>>>()
|
||||
.get::<Mutex<PrivateSurfaceData>>()
|
||||
.unwrap();
|
||||
let mut data_guard = data_mutex.lock().unwrap();
|
||||
let data_guard = &mut *data_guard;
|
||||
// call the filter on ourselves
|
||||
match filter(surface, &mut data_guard.attributes, &mut data_guard.role, initial) {
|
||||
match filter(surface, &data_guard.public_data, initial) {
|
||||
TraversalAction::DoChildren(t) => {
|
||||
// loop over children
|
||||
if reverse {
|
||||
for c in data_guard.children.iter().rev() {
|
||||
if c.as_ref().equals(surface.as_ref()) {
|
||||
processor(surface, &mut data_guard.attributes, &mut data_guard.role, initial);
|
||||
processor(surface, &data_guard.public_data, initial);
|
||||
} else if !Self::map(c, &t, filter, processor, post_filter, true) {
|
||||
return false;
|
||||
}
|
||||
|
@ -434,17 +468,17 @@ impl<R: 'static> SurfaceData<R> {
|
|||
} else {
|
||||
for c in &data_guard.children {
|
||||
if c.as_ref().equals(surface.as_ref()) {
|
||||
processor(surface, &mut data_guard.attributes, &mut data_guard.role, initial);
|
||||
processor(surface, &data_guard.public_data, initial);
|
||||
} else if !Self::map(c, &t, filter, processor, post_filter, false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
post_filter(surface, &mut data_guard.attributes, &mut data_guard.role, initial)
|
||||
post_filter(surface, &data_guard.public_data, initial)
|
||||
}
|
||||
TraversalAction::SkipChildren => {
|
||||
// still process ourselves
|
||||
processor(surface, &mut data_guard.attributes, &mut data_guard.role, initial);
|
||||
processor(surface, &data_guard.public_data, initial);
|
||||
true
|
||||
}
|
||||
TraversalAction::Break => false,
|
||||
|
|
|
@ -6,14 +6,13 @@ use wayland_server::{
|
|||
};
|
||||
|
||||
use crate::wayland::{
|
||||
compositor::{roles::Role, CompositorToken},
|
||||
seat::{AxisFrame, GrabStartData, PointerGrab, PointerInnerHandle, Seat},
|
||||
Serial,
|
||||
};
|
||||
|
||||
use super::{with_source_metadata, DataDeviceData, DnDIconRole, SeatData};
|
||||
use super::{with_source_metadata, DataDeviceData, SeatData};
|
||||
|
||||
pub(crate) struct DnDGrab<R> {
|
||||
pub(crate) struct DnDGrab {
|
||||
start_data: GrabStartData,
|
||||
data_source: Option<wl_data_source::WlDataSource>,
|
||||
current_focus: Option<wl_surface::WlSurface>,
|
||||
|
@ -22,20 +21,18 @@ pub(crate) struct DnDGrab<R> {
|
|||
icon: Option<wl_surface::WlSurface>,
|
||||
origin: wl_surface::WlSurface,
|
||||
callback: Rc<RefCell<dyn FnMut(super::DataDeviceEvent)>>,
|
||||
token: CompositorToken<R>,
|
||||
seat: Seat,
|
||||
}
|
||||
|
||||
impl<R: Role<DnDIconRole> + 'static> DnDGrab<R> {
|
||||
impl DnDGrab {
|
||||
pub(crate) fn new(
|
||||
start_data: GrabStartData,
|
||||
source: Option<wl_data_source::WlDataSource>,
|
||||
origin: wl_surface::WlSurface,
|
||||
seat: Seat,
|
||||
icon: Option<wl_surface::WlSurface>,
|
||||
token: CompositorToken<R>,
|
||||
callback: Rc<RefCell<dyn FnMut(super::DataDeviceEvent)>>,
|
||||
) -> DnDGrab<R> {
|
||||
) -> DnDGrab {
|
||||
DnDGrab {
|
||||
start_data,
|
||||
data_source: source,
|
||||
|
@ -45,13 +42,12 @@ impl<R: Role<DnDIconRole> + 'static> DnDGrab<R> {
|
|||
origin,
|
||||
icon,
|
||||
callback,
|
||||
token,
|
||||
seat,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Role<DnDIconRole> + 'static> PointerGrab for DnDGrab<R> {
|
||||
impl PointerGrab for DnDGrab {
|
||||
fn motion(
|
||||
&mut self,
|
||||
_handle: &mut PointerInnerHandle<'_>,
|
||||
|
@ -211,11 +207,7 @@ impl<R: Role<DnDIconRole> + 'static> PointerGrab for DnDGrab<R> {
|
|||
}
|
||||
}
|
||||
(&mut *self.callback.borrow_mut())(super::DataDeviceEvent::DnDDropped);
|
||||
if let Some(icon) = self.icon.take() {
|
||||
if icon.as_ref().is_alive() {
|
||||
self.token.remove_role::<super::DnDIconRole>(&icon).unwrap();
|
||||
}
|
||||
}
|
||||
self.icon = None;
|
||||
// in all cases abandon the drop
|
||||
// no more buttons are pressed, release the grab
|
||||
handle.unset_grab(serial, time);
|
||||
|
|
|
@ -25,30 +25,22 @@
|
|||
//! - the freestanding function [`start_dnd`] allows you to initiate a drag'n'drop event from the compositor
|
||||
//! itself and receive interactions of clients with it via an other dedicated callback.
|
||||
//!
|
||||
//! The module also defines the `DnDIconRole` that you need to insert into your compositor roles enum, to
|
||||
//! represent surfaces that are used as a DnD icon.
|
||||
//! The module defines the role `"dnd_icon"` that is assigned to surfaces used as drag'n'drop icons.
|
||||
//!
|
||||
//! ## Initialization
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate wayland_server;
|
||||
//! # #[macro_use] extern crate smithay;
|
||||
//! use smithay::wayland::data_device::{init_data_device, default_action_chooser, DnDIconRole};
|
||||
//! # use smithay::wayland::compositor::compositor_init;
|
||||
//!
|
||||
//! // You need to insert the `DndIconRole` into your roles, to handle requests from clients
|
||||
//! // to set a surface as a dnd icon
|
||||
//! define_roles!(Roles => [DnDIcon, DnDIconRole]);
|
||||
//!
|
||||
//! # let mut display = wayland_server::Display::new();
|
||||
//! # let (compositor_token, _, _) = compositor_init::<Roles, _, _>(&mut display, |_, _, _, _| {}, None);
|
||||
//! // init the data device:
|
||||
//! init_data_device(
|
||||
//! &mut display, // the display
|
||||
//! |dnd_event| { /* a callback to react to client DnD/selection actions */ },
|
||||
//! default_action_chooser, // a closure to choose the DnD action depending on clients
|
||||
//! // negociation
|
||||
//! compositor_token.clone(), // a compositor token
|
||||
//! None // insert a logger here
|
||||
//! );
|
||||
//! ```
|
||||
|
@ -65,7 +57,7 @@ use wayland_server::{
|
|||
};
|
||||
|
||||
use crate::wayland::{
|
||||
compositor::{roles::Role, CompositorToken},
|
||||
compositor,
|
||||
seat::{GrabStartData, Seat},
|
||||
Serial,
|
||||
};
|
||||
|
@ -77,6 +69,8 @@ mod server_dnd_grab;
|
|||
pub use self::data_source::{with_source_metadata, SourceMetadata};
|
||||
pub use self::server_dnd_grab::ServerDndEvent;
|
||||
|
||||
static DND_ICON_ROLE: &str = "dnd_icon";
|
||||
|
||||
/// Events that are generated by interactions of the clients with the data device
|
||||
#[derive(Debug)]
|
||||
pub enum DataDeviceEvent {
|
||||
|
@ -275,17 +269,15 @@ impl SeatData {
|
|||
/// and the second argument is the preferred action reported by the target. If no action should be
|
||||
/// chosen (and thus the drag'n'drop should abort on drop), return
|
||||
/// [`DndAction::empty()`](wayland_server::protocol::wl_data_device_manager::DndAction::empty).
|
||||
pub fn init_data_device<F, C, R, L>(
|
||||
pub fn init_data_device<F, C, L>(
|
||||
display: &mut Display,
|
||||
callback: C,
|
||||
action_choice: F,
|
||||
token: CompositorToken<R>,
|
||||
logger: L,
|
||||
) -> Global<wl_data_device_manager::WlDataDeviceManager>
|
||||
where
|
||||
F: FnMut(DndAction, DndAction) -> DndAction + 'static,
|
||||
C: FnMut(DataDeviceEvent) + 'static,
|
||||
R: Role<DnDIconRole> + 'static,
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
let log = crate::slog_or_fallback(logger).new(o!("smithay_module" => "data_device_mgr"));
|
||||
|
@ -294,7 +286,7 @@ where
|
|||
display.create_global(
|
||||
3,
|
||||
Filter::new(move |(ddm, _version), _, _| {
|
||||
implement_ddm(ddm, callback.clone(), action_choice.clone(), token, log.clone());
|
||||
implement_ddm(ddm, callback.clone(), action_choice.clone(), log.clone());
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
@ -371,17 +363,15 @@ pub fn start_dnd<C>(
|
|||
}
|
||||
}
|
||||
|
||||
fn implement_ddm<F, C, R>(
|
||||
fn implement_ddm<F, C>(
|
||||
ddm: Main<wl_data_device_manager::WlDataDeviceManager>,
|
||||
callback: Rc<RefCell<C>>,
|
||||
action_choice: Rc<RefCell<F>>,
|
||||
token: CompositorToken<R>,
|
||||
log: ::slog::Logger,
|
||||
) -> wl_data_device_manager::WlDataDeviceManager
|
||||
where
|
||||
F: FnMut(DndAction, DndAction) -> DndAction + 'static,
|
||||
C: FnMut(DataDeviceEvent) + 'static,
|
||||
R: Role<DnDIconRole> + 'static,
|
||||
{
|
||||
use self::wl_data_device_manager::Request;
|
||||
ddm.quick_assign(move |_ddm, req, _data| match req {
|
||||
|
@ -399,7 +389,6 @@ where
|
|||
seat.clone(),
|
||||
callback.clone(),
|
||||
action_choice.clone(),
|
||||
token,
|
||||
log.clone(),
|
||||
);
|
||||
seat_data.borrow_mut().known_devices.push(data_device);
|
||||
|
@ -419,18 +408,16 @@ struct DataDeviceData {
|
|||
action_choice: Rc<RefCell<dyn FnMut(DndAction, DndAction) -> DndAction + 'static>>,
|
||||
}
|
||||
|
||||
fn implement_data_device<F, C, R>(
|
||||
fn implement_data_device<F, C>(
|
||||
dd: Main<wl_data_device::WlDataDevice>,
|
||||
seat: Seat,
|
||||
callback: Rc<RefCell<C>>,
|
||||
action_choice: Rc<RefCell<F>>,
|
||||
token: CompositorToken<R>,
|
||||
log: ::slog::Logger,
|
||||
) -> wl_data_device::WlDataDevice
|
||||
where
|
||||
F: FnMut(DndAction, DndAction) -> DndAction + 'static,
|
||||
C: FnMut(DataDeviceEvent) + 'static,
|
||||
R: Role<DnDIconRole> + 'static,
|
||||
{
|
||||
use self::wl_data_device::Request;
|
||||
let dd_data = DataDeviceData {
|
||||
|
@ -449,7 +436,7 @@ where
|
|||
if let Some(pointer) = seat.get_pointer() {
|
||||
if pointer.has_grab(serial) {
|
||||
if let Some(ref icon) = icon {
|
||||
if token.give_role::<DnDIconRole>(icon).is_err() {
|
||||
if compositor::give_role(icon, DND_ICON_ROLE).is_err() {
|
||||
dd.as_ref().post_error(
|
||||
wl_data_device::Error::Role as u32,
|
||||
"Given surface already has an other role".into(),
|
||||
|
@ -470,7 +457,6 @@ where
|
|||
origin,
|
||||
seat.clone(),
|
||||
icon,
|
||||
token,
|
||||
callback.clone(),
|
||||
),
|
||||
serial,
|
||||
|
|
|
@ -265,7 +265,7 @@ where
|
|||
format,
|
||||
DmabufFlags::from_bits_truncate(flags),
|
||||
);
|
||||
let planes = ::std::mem::replace(&mut self.pending_planes, Vec::new());
|
||||
let planes = std::mem::take(&mut self.pending_planes);
|
||||
for (i, plane) in planes.into_iter().enumerate() {
|
||||
let offset = plane.offset;
|
||||
let stride = plane.stride;
|
||||
|
@ -355,7 +355,7 @@ where
|
|||
format,
|
||||
DmabufFlags::from_bits_truncate(flags),
|
||||
);
|
||||
let planes = ::std::mem::replace(&mut self.pending_planes, Vec::new());
|
||||
let planes = ::std::mem::take(&mut self.pending_planes);
|
||||
for (i, plane) in planes.into_iter().enumerate() {
|
||||
let offset = plane.offset;
|
||||
let stride = plane.stride;
|
||||
|
|
|
@ -21,60 +21,32 @@
|
|||
//!
|
||||
//! ```
|
||||
//! # extern crate wayland_server;
|
||||
//! # #[macro_use] extern crate smithay;
|
||||
//! #
|
||||
//! # use smithay::wayland::compositor::roles::*;
|
||||
//! # use smithay::wayland::compositor::CompositorToken;
|
||||
//! use smithay::wayland::explicit_synchronization::*;
|
||||
//! # define_roles!(MyRoles);
|
||||
//! #
|
||||
//! # let mut display = wayland_server::Display::new();
|
||||
//! # let (compositor_token, _, _) = smithay::wayland::compositor::compositor_init::<MyRoles, _, _>(
|
||||
//! # &mut display,
|
||||
//! # |_, _, _, _| {},
|
||||
//! # None
|
||||
//! # );
|
||||
//! init_explicit_synchronization_global(
|
||||
//! &mut display,
|
||||
//! compositor_token,
|
||||
//! None /* You can insert a logger here */
|
||||
//! );
|
||||
//! ```
|
||||
//!
|
||||
//! Then when handling a surface commit, you can retrieve the synchronization information for the surface
|
||||
//! data:
|
||||
//! ```no_run
|
||||
//! Then when handling a surface commit, you can retrieve the synchronization information for the surface states:
|
||||
//! ```
|
||||
//! # extern crate wayland_server;
|
||||
//! # #[macro_use] extern crate smithay;
|
||||
//! #
|
||||
//! # use wayland_server::protocol::wl_surface::WlSurface;
|
||||
//! # use smithay::wayland::compositor::CompositorToken;
|
||||
//! # use smithay::wayland::explicit_synchronization::*;
|
||||
//! #
|
||||
//! # fn dummy_function<R: 'static>(surface: &WlSurface, compositor_token: CompositorToken<R>) {
|
||||
//! compositor_token.with_surface_data(&surface, |surface_attributes| {
|
||||
//! // While you retrieve the surface data from the commit ...
|
||||
//! // Check the explicit synchronization data:
|
||||
//! match get_explicit_synchronization_state(surface_attributes) {
|
||||
//! Ok(sync_state) => {
|
||||
//! /* This surface is explicitly synchronized, you need to handle
|
||||
//! the contents of sync_state
|
||||
//! */
|
||||
//! },
|
||||
//! Err(NoExplicitSync) => {
|
||||
//! /* This surface is not explicitly synchronized, nothing more to do
|
||||
//! */
|
||||
//! }
|
||||
//! }
|
||||
//! # fn dummy_function<R: 'static>(surface: &WlSurface) {
|
||||
//! use smithay::wayland::compositor::with_states;
|
||||
//! with_states(&surface, |states| {
|
||||
//! let explicit_sync_state = states.cached_state.current::<ExplicitSyncState>();
|
||||
//! /* process the explicit_sync_state */
|
||||
//! });
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
ops::{Deref as _, DerefMut as _},
|
||||
os::unix::io::RawFd,
|
||||
};
|
||||
use std::{cell::RefCell, ops::Deref as _, os::unix::io::RawFd};
|
||||
|
||||
use wayland_protocols::unstable::linux_explicit_synchronization::v1::server::{
|
||||
zwp_linux_buffer_release_v1::ZwpLinuxBufferReleaseV1,
|
||||
|
@ -83,7 +55,7 @@ use wayland_protocols::unstable::linux_explicit_synchronization::v1::server::{
|
|||
};
|
||||
use wayland_server::{protocol::wl_surface::WlSurface, Display, Filter, Global, Main};
|
||||
|
||||
use crate::wayland::compositor::{CompositorToken, SurfaceAttributes};
|
||||
use super::compositor::{with_states, Cacheable, SurfaceData};
|
||||
|
||||
/// An object to signal end of use of a buffer
|
||||
#[derive(Debug)]
|
||||
|
@ -112,6 +84,9 @@ impl ExplicitBufferRelease {
|
|||
/// The client is not required to fill both. `acquire` being `None` means that you don't need to wait
|
||||
/// before acessing the buffer, `release` being `None` means that the client does not require additionnal
|
||||
/// signaling that you are finished (you still need to send `wl_buffer.release`).
|
||||
///
|
||||
/// When processing the current state, the whould [`Option::take`] the values from it. Otherwise they'll
|
||||
/// be treated as unused and released when overwritten by the next client commit.
|
||||
#[derive(Debug)]
|
||||
pub struct ExplicitSyncState {
|
||||
/// An acquire `dma_fence` object, that you should wait on before accessing the contents of the
|
||||
|
@ -122,26 +97,37 @@ pub struct ExplicitSyncState {
|
|||
pub release: Option<ExplicitBufferRelease>,
|
||||
}
|
||||
|
||||
struct InternalState {
|
||||
sync_state: ExplicitSyncState,
|
||||
sync_resource: ZwpLinuxSurfaceSynchronizationV1,
|
||||
impl Default for ExplicitSyncState {
|
||||
fn default() -> Self {
|
||||
ExplicitSyncState {
|
||||
acquire: None,
|
||||
release: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cacheable for ExplicitSyncState {
|
||||
fn commit(&mut self) -> Self {
|
||||
std::mem::take(self)
|
||||
}
|
||||
fn merge_into(mut self, into: &mut Self) {
|
||||
if self.acquire.is_some() {
|
||||
if let Some(fd) = std::mem::replace(&mut into.acquire, self.acquire.take()) {
|
||||
// close the unused fd
|
||||
let _ = nix::unistd::close(fd);
|
||||
}
|
||||
}
|
||||
if self.release.is_some() {
|
||||
if let Some(release) = std::mem::replace(&mut into.release, self.release.take()) {
|
||||
// release the overriden state
|
||||
release.immediate_release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ESUserData {
|
||||
state: RefCell<Option<InternalState>>,
|
||||
}
|
||||
|
||||
impl ESUserData {
|
||||
fn take_state(&self) -> Option<ExplicitSyncState> {
|
||||
if let Some(state) = self.state.borrow_mut().deref_mut() {
|
||||
Some(ExplicitSyncState {
|
||||
acquire: state.sync_state.acquire.take(),
|
||||
release: state.sync_state.release.take(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
state: RefCell<Option<ZwpLinuxSurfaceSynchronizationV1>>,
|
||||
}
|
||||
|
||||
/// Possible errors you can send to an ill-behaving clients
|
||||
|
@ -167,42 +153,24 @@ impl std::fmt::Display for NoExplicitSync {
|
|||
|
||||
impl std::error::Error for NoExplicitSync {}
|
||||
|
||||
/// Retrieve the explicit synchronization state commited by the client
|
||||
///
|
||||
/// This state can contain an acquire fence and a release object, for synchronization (see module-level docs).
|
||||
///
|
||||
/// This function will clear the pending state, preparing the surface for the next commit, as a result you
|
||||
/// should always call it on surface commit to avoid getting out-of-sync with the client.
|
||||
///
|
||||
/// This function returns an error if the client has not setup explicit synchronization for this surface.
|
||||
pub fn get_explicit_synchronization_state(
|
||||
attrs: &mut SurfaceAttributes,
|
||||
) -> Result<ExplicitSyncState, NoExplicitSync> {
|
||||
attrs
|
||||
.user_data
|
||||
.get::<ESUserData>()
|
||||
.and_then(|s| s.take_state())
|
||||
.ok_or(NoExplicitSync)
|
||||
}
|
||||
|
||||
/// Send a synchronization error to a client
|
||||
///
|
||||
/// See the enum definition for possible errors. These errors are protocol errors, meaning that
|
||||
/// the client associated with this `SurfaceAttributes` will be killed as a result of calling this
|
||||
/// function.
|
||||
pub fn send_explicit_synchronization_error(attrs: &SurfaceAttributes, error: ExplicitSyncError) {
|
||||
if let Some(ref data) = attrs.user_data.get::<ESUserData>() {
|
||||
if let Some(state) = data.state.borrow().deref() {
|
||||
pub fn send_explicit_synchronization_error(attrs: &SurfaceData, error: ExplicitSyncError) {
|
||||
if let Some(ref data) = attrs.data_map.get::<ESUserData>() {
|
||||
if let Some(sync_resource) = data.state.borrow().deref() {
|
||||
match error {
|
||||
ExplicitSyncError::InvalidFence => state.sync_resource.as_ref().post_error(
|
||||
ExplicitSyncError::InvalidFence => sync_resource.as_ref().post_error(
|
||||
zwp_linux_surface_synchronization_v1::Error::InvalidFence as u32,
|
||||
"The fence specified by the client could not be imported.".into(),
|
||||
),
|
||||
ExplicitSyncError::UnsupportedBuffer => state.sync_resource.as_ref().post_error(
|
||||
ExplicitSyncError::UnsupportedBuffer => sync_resource.as_ref().post_error(
|
||||
zwp_linux_surface_synchronization_v1::Error::UnsupportedBuffer as u32,
|
||||
"The buffer does not support explicit synchronization.".into(),
|
||||
),
|
||||
ExplicitSyncError::NoBuffer => state.sync_resource.as_ref().post_error(
|
||||
ExplicitSyncError::NoBuffer => sync_resource.as_ref().post_error(
|
||||
zwp_linux_surface_synchronization_v1::Error::NoBuffer as u32,
|
||||
"No buffer was attached.".into(),
|
||||
),
|
||||
|
@ -214,14 +182,12 @@ pub fn send_explicit_synchronization_error(attrs: &SurfaceAttributes, error: Exp
|
|||
/// Initialize the explicit synchronization global
|
||||
///
|
||||
/// See module-level documentation for its use.
|
||||
pub fn init_explicit_synchronization_global<R, L>(
|
||||
pub fn init_explicit_synchronization_global<L>(
|
||||
display: &mut Display,
|
||||
compositor: CompositorToken<R>,
|
||||
logger: L,
|
||||
) -> Global<ZwpLinuxExplicitSynchronizationV1>
|
||||
where
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
R: 'static,
|
||||
{
|
||||
let _log =
|
||||
crate::slog_or_fallback(logger).new(o!("smithay_module" => "wayland_explicit_synchronization"));
|
||||
|
@ -236,16 +202,17 @@ where
|
|||
surface,
|
||||
} = req
|
||||
{
|
||||
let exists = compositor.with_surface_data(&surface, |attrs| {
|
||||
attrs.user_data.insert_if_missing(|| ESUserData {
|
||||
let exists = with_states(&surface, |states| {
|
||||
states.data_map.insert_if_missing(|| ESUserData {
|
||||
state: RefCell::new(None),
|
||||
});
|
||||
attrs
|
||||
.user_data
|
||||
states
|
||||
.data_map
|
||||
.get::<ESUserData>()
|
||||
.map(|ud| ud.state.borrow().is_some())
|
||||
.unwrap()
|
||||
});
|
||||
})
|
||||
.unwrap_or(false);
|
||||
if exists {
|
||||
explicit_sync.as_ref().post_error(
|
||||
zwp_linux_explicit_synchronization_v1::Error::SynchronizationExists as u32,
|
||||
|
@ -253,17 +220,12 @@ where
|
|||
);
|
||||
return;
|
||||
}
|
||||
let surface_sync = implement_surface_sync(id, surface.clone(), compositor);
|
||||
compositor.with_surface_data(&surface, |attrs| {
|
||||
let data = attrs.user_data.get::<ESUserData>().unwrap();
|
||||
*data.state.borrow_mut() = Some(InternalState {
|
||||
sync_state: ExplicitSyncState {
|
||||
acquire: None,
|
||||
release: None,
|
||||
},
|
||||
sync_resource: surface_sync,
|
||||
});
|
||||
});
|
||||
let surface_sync = implement_surface_sync(id, surface.clone());
|
||||
with_states(&surface, |states| {
|
||||
let data = states.data_map.get::<ESUserData>().unwrap();
|
||||
*data.state.borrow_mut() = Some(surface_sync);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -271,14 +233,10 @@ where
|
|||
)
|
||||
}
|
||||
|
||||
fn implement_surface_sync<R>(
|
||||
fn implement_surface_sync(
|
||||
id: Main<ZwpLinuxSurfaceSynchronizationV1>,
|
||||
surface: WlSurface,
|
||||
compositor: CompositorToken<R>,
|
||||
) -> ZwpLinuxSurfaceSynchronizationV1
|
||||
where
|
||||
R: 'static,
|
||||
{
|
||||
) -> ZwpLinuxSurfaceSynchronizationV1 {
|
||||
id.quick_assign(move |surface_sync, req, _| match req {
|
||||
zwp_linux_surface_synchronization_v1::Request::SetAcquireFence { fd } => {
|
||||
if !surface.as_ref().is_alive() {
|
||||
|
@ -287,19 +245,18 @@ where
|
|||
"The associated wl_surface was destroyed.".into(),
|
||||
)
|
||||
}
|
||||
compositor.with_surface_data(&surface, |attrs| {
|
||||
let data = attrs.user_data.get::<ESUserData>().unwrap();
|
||||
if let Some(state) = data.state.borrow_mut().deref_mut() {
|
||||
if state.sync_state.acquire.is_some() {
|
||||
with_states(&surface, |states| {
|
||||
let mut pending = states.cached_state.pending::<ExplicitSyncState>();
|
||||
if pending.acquire.is_some() {
|
||||
surface_sync.as_ref().post_error(
|
||||
zwp_linux_surface_synchronization_v1::Error::DuplicateFence as u32,
|
||||
"Multiple fences added for a single surface commit.".into(),
|
||||
)
|
||||
} else {
|
||||
state.sync_state.acquire = Some(fd);
|
||||
pending.acquire = Some(fd);
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
zwp_linux_surface_synchronization_v1::Request::GetRelease { release } => {
|
||||
if !surface.as_ref().is_alive() {
|
||||
|
@ -308,30 +265,30 @@ where
|
|||
"The associated wl_surface was destroyed.".into(),
|
||||
)
|
||||
}
|
||||
compositor.with_surface_data(&surface, |attrs| {
|
||||
let data = attrs.user_data.get::<ESUserData>().unwrap();
|
||||
if let Some(state) = data.state.borrow_mut().deref_mut() {
|
||||
if state.sync_state.acquire.is_some() {
|
||||
with_states(&surface, |states| {
|
||||
let mut pending = states.cached_state.pending::<ExplicitSyncState>();
|
||||
if pending.release.is_some() {
|
||||
surface_sync.as_ref().post_error(
|
||||
zwp_linux_surface_synchronization_v1::Error::DuplicateRelease as u32,
|
||||
"Multiple releases added for a single surface commit.".into(),
|
||||
)
|
||||
} else {
|
||||
release.quick_assign(|_, _, _| {});
|
||||
state.sync_state.release = Some(ExplicitBufferRelease {
|
||||
pending.release = Some(ExplicitBufferRelease {
|
||||
release: release.deref().clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
zwp_linux_surface_synchronization_v1::Request::Destroy => {
|
||||
// disable the ESUserData
|
||||
compositor.with_surface_data(&surface, |attrs| {
|
||||
if let Some(ref mut data) = attrs.user_data.get::<ESUserData>() {
|
||||
with_states(&surface, |states| {
|
||||
if let Some(ref mut data) = states.data_map.get::<ESUserData>() {
|
||||
*data.state.borrow_mut() = None;
|
||||
}
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
_ => (),
|
||||
});
|
||||
|
|
|
@ -225,7 +225,7 @@ impl Output {
|
|||
pub fn set_preferred(&self, mode: Mode) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
inner.preferred_mode = Some(mode);
|
||||
if inner.modes.iter().find(|&m| *m == mode).is_none() {
|
||||
if inner.modes.iter().all(|&m| m != mode) {
|
||||
inner.modes.push(mode);
|
||||
}
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ impl Output {
|
|||
/// Adds a mode to the list of known modes to this output
|
||||
pub fn add_mode(&self, mode: Mode) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
if inner.modes.iter().find(|&m| *m == mode).is_none() {
|
||||
if inner.modes.iter().all(|&m| m != mode) {
|
||||
inner.modes.push(mode);
|
||||
}
|
||||
}
|
||||
|
@ -270,7 +270,7 @@ impl Output {
|
|||
) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
if let Some(mode) = new_mode {
|
||||
if inner.modes.iter().find(|&m| *m == mode).is_none() {
|
||||
if inner.modes.iter().all(|&m| m != mode) {
|
||||
inner.modes.push(mode);
|
||||
}
|
||||
inner.current_mode = new_mode;
|
||||
|
|
|
@ -9,21 +9,13 @@
|
|||
//!
|
||||
//! ```
|
||||
//! # extern crate wayland_server;
|
||||
//! # #[macro_use] extern crate smithay;
|
||||
//! use smithay::wayland::seat::{Seat, CursorImageRole};
|
||||
//! # use smithay::wayland::compositor::compositor_init;
|
||||
//!
|
||||
//! // You need to insert the `CursorImageRole` into your roles, to handle requests from clients
|
||||
//! // to set a surface as a cursor image
|
||||
//! define_roles!(Roles => [CursorImage, CursorImageRole]);
|
||||
//! use smithay::wayland::seat::Seat;
|
||||
//!
|
||||
//! # let mut display = wayland_server::Display::new();
|
||||
//! # let (compositor_token, _, _) = compositor_init::<Roles, _, _>(&mut display, |_, _, _, _| {}, None);
|
||||
//! // insert the seat:
|
||||
//! let (seat, seat_global) = Seat::new(
|
||||
//! &mut display, // the display
|
||||
//! "seat-0".into(), // the name of the seat, will be advertized to clients
|
||||
//! compositor_token.clone(), // the compositor token
|
||||
//! None // insert a logger here
|
||||
//! );
|
||||
//! ```
|
||||
|
@ -32,13 +24,15 @@
|
|||
//!
|
||||
//! Once the seat is initialized, you can add capabilities to it.
|
||||
//!
|
||||
//! Currently, only pointer and keyboard capabilities are supported by
|
||||
//! smithay.
|
||||
//! Currently, only pointer and keyboard capabilities are supported by smithay.
|
||||
//!
|
||||
//! You can add these capabilities via methods of the [`Seat`] struct:
|
||||
//! [`add_keyboard`](Seat::add_keyboard), [`add_pointer`](Seat::add_pointer).
|
||||
//! [`Seat::add_keyboard`] and [`Seat::add_pointer`].
|
||||
//! These methods return handles that can be cloned and sent across thread, so you can keep one around
|
||||
//! in your event-handling code to forward inputs to your clients.
|
||||
//!
|
||||
//! This module further defines the `"cursor_image"` role, that is assigned to surfaces used by clients
|
||||
//! to change the cursor icon.
|
||||
|
||||
use std::{cell::RefCell, fmt, ops::Deref as _, rc::Rc};
|
||||
|
||||
|
@ -48,13 +42,11 @@ mod pointer;
|
|||
pub use self::{
|
||||
keyboard::{keysyms, Error as KeyboardError, KeyboardHandle, Keysym, ModifiersState, XkbConfig},
|
||||
pointer::{
|
||||
AxisFrame, CursorImageRole, CursorImageStatus, GrabStartData, PointerGrab, PointerHandle,
|
||||
AxisFrame, CursorImageAttributes, CursorImageStatus, GrabStartData, PointerGrab, PointerHandle,
|
||||
PointerInnerHandle,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::wayland::compositor::{roles::Role, CompositorToken};
|
||||
|
||||
use wayland_server::{
|
||||
protocol::{wl_seat, wl_surface},
|
||||
Display, Filter, Global, Main, UserDataMap,
|
||||
|
@ -130,14 +122,8 @@ impl Seat {
|
|||
/// You are provided with the state token to retrieve it (allowing
|
||||
/// you to add or remove capabilities from it), and the global handle,
|
||||
/// in case you want to remove it.
|
||||
pub fn new<R, L>(
|
||||
display: &mut Display,
|
||||
name: String,
|
||||
token: CompositorToken<R>,
|
||||
logger: L,
|
||||
) -> (Seat, Global<wl_seat::WlSeat>)
|
||||
pub fn new<L>(display: &mut Display, name: String, logger: L) -> (Seat, Global<wl_seat::WlSeat>)
|
||||
where
|
||||
R: Role<CursorImageRole> + 'static,
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
let log = crate::slog_or_fallback(logger);
|
||||
|
@ -155,7 +141,7 @@ impl Seat {
|
|||
let global = display.create_global(
|
||||
5,
|
||||
Filter::new(move |(new_seat, _version), _, _| {
|
||||
let seat = implement_seat(new_seat, arc.clone(), token);
|
||||
let seat = implement_seat(new_seat, arc.clone());
|
||||
let mut inner = arc.inner.borrow_mut();
|
||||
if seat.as_ref().version() >= 2 {
|
||||
seat.name(arc.name.clone());
|
||||
|
@ -197,32 +183,25 @@ impl Seat {
|
|||
///
|
||||
/// ```
|
||||
/// # extern crate wayland_server;
|
||||
/// # #[macro_use] extern crate smithay;
|
||||
/// #
|
||||
/// # use smithay::wayland::{seat::{Seat, CursorImageRole}, compositor::compositor_init};
|
||||
/// #
|
||||
/// # define_roles!(Roles => [CursorImage, CursorImageRole]);
|
||||
/// # use smithay::wayland::seat::Seat;
|
||||
/// #
|
||||
/// # let mut display = wayland_server::Display::new();
|
||||
/// # let (compositor_token, _, _) = compositor_init::<Roles, _, _>(&mut display, |_, _, _, _| {}, None);
|
||||
/// # let (mut seat, seat_global) = Seat::new(
|
||||
/// # &mut display,
|
||||
/// # "seat-0".into(),
|
||||
/// # compositor_token.clone(),
|
||||
/// # None
|
||||
/// # );
|
||||
/// let pointer_handle = seat.add_pointer(
|
||||
/// compositor_token.clone(),
|
||||
/// |new_status| { /* a closure handling requests from clients tot change the cursor icon */ }
|
||||
/// );
|
||||
/// ```
|
||||
pub fn add_pointer<R, F>(&mut self, token: CompositorToken<R>, cb: F) -> PointerHandle
|
||||
pub fn add_pointer<F>(&mut self, cb: F) -> PointerHandle
|
||||
where
|
||||
R: Role<CursorImageRole> + 'static,
|
||||
F: FnMut(CursorImageStatus) + 'static,
|
||||
{
|
||||
let mut inner = self.arc.inner.borrow_mut();
|
||||
let pointer = self::pointer::create_pointer_handler(token, cb);
|
||||
let pointer = self::pointer::create_pointer_handler(cb);
|
||||
if inner.pointer.is_some() {
|
||||
// there is already a pointer, remove it and notify the clients
|
||||
// of the change
|
||||
|
@ -344,21 +323,14 @@ impl ::std::cmp::PartialEq for Seat {
|
|||
}
|
||||
}
|
||||
|
||||
fn implement_seat<R>(
|
||||
seat: Main<wl_seat::WlSeat>,
|
||||
arc: Rc<SeatRc>,
|
||||
token: CompositorToken<R>,
|
||||
) -> wl_seat::WlSeat
|
||||
where
|
||||
R: Role<CursorImageRole> + 'static,
|
||||
{
|
||||
fn implement_seat(seat: Main<wl_seat::WlSeat>, arc: Rc<SeatRc>) -> wl_seat::WlSeat {
|
||||
let dest_arc = arc.clone();
|
||||
seat.quick_assign(move |seat, request, _| {
|
||||
let arc = seat.as_ref().user_data().get::<Rc<SeatRc>>().unwrap();
|
||||
let inner = arc.inner.borrow_mut();
|
||||
match request {
|
||||
wl_seat::Request::GetPointer { id } => {
|
||||
let pointer = self::pointer::implement_pointer(id, inner.pointer.as_ref(), token);
|
||||
let pointer = self::pointer::implement_pointer(id, inner.pointer.as_ref());
|
||||
if let Some(ref ptr_handle) = inner.pointer {
|
||||
ptr_handle.new_pointer(pointer);
|
||||
} else {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{cell::RefCell, fmt, ops::Deref as _, rc::Rc};
|
||||
use std::{cell::RefCell, fmt, ops::Deref as _, rc::Rc, sync::Mutex};
|
||||
|
||||
use wayland_server::{
|
||||
protocol::{
|
||||
|
@ -8,12 +8,14 @@ use wayland_server::{
|
|||
Filter, Main,
|
||||
};
|
||||
|
||||
use crate::wayland::compositor::{roles::Role, CompositorToken};
|
||||
use crate::wayland::compositor;
|
||||
use crate::wayland::Serial;
|
||||
|
||||
static CURSOR_IMAGE_ROLE: &str = "cursor_image";
|
||||
|
||||
/// The role representing a surface set as the pointer cursor
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
pub struct CursorImageRole {
|
||||
pub struct CursorImageAttributes {
|
||||
/// Location of the hotspot of the pointer in the surface
|
||||
pub hotspot: (i32, i32),
|
||||
}
|
||||
|
@ -72,9 +74,8 @@ impl fmt::Debug for PointerInternal {
|
|||
}
|
||||
|
||||
impl PointerInternal {
|
||||
fn new<F, R>(token: CompositorToken<R>, mut cb: F) -> PointerInternal
|
||||
fn new<F>(mut cb: F) -> PointerInternal
|
||||
where
|
||||
R: Role<CursorImageRole> + 'static,
|
||||
F: FnMut(CursorImageStatus) + 'static,
|
||||
{
|
||||
let mut old_status = CursorImageStatus::Default;
|
||||
|
@ -86,11 +87,7 @@ impl PointerInternal {
|
|||
CursorImageStatus::Image(ref new_surface) if new_surface == &surface => {
|
||||
// don't remove the role, we are just re-binding the same surface
|
||||
}
|
||||
_ => {
|
||||
if surface.as_ref().is_alive() {
|
||||
token.remove_role::<CursorImageRole>(&surface).unwrap();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
cb(new_status)
|
||||
|
@ -566,24 +563,16 @@ impl AxisFrame {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create_pointer_handler<F, R>(token: CompositorToken<R>, cb: F) -> PointerHandle
|
||||
pub(crate) fn create_pointer_handler<F>(cb: F) -> PointerHandle
|
||||
where
|
||||
R: Role<CursorImageRole> + 'static,
|
||||
F: FnMut(CursorImageStatus) + 'static,
|
||||
{
|
||||
PointerHandle {
|
||||
inner: Rc::new(RefCell::new(PointerInternal::new(token, cb))),
|
||||
inner: Rc::new(RefCell::new(PointerInternal::new(cb))),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn implement_pointer<R>(
|
||||
pointer: Main<WlPointer>,
|
||||
handle: Option<&PointerHandle>,
|
||||
token: CompositorToken<R>,
|
||||
) -> WlPointer
|
||||
where
|
||||
R: Role<CursorImageRole> + 'static,
|
||||
{
|
||||
pub(crate) fn implement_pointer(pointer: Main<WlPointer>, handle: Option<&PointerHandle>) -> WlPointer {
|
||||
let inner = handle.map(|h| h.inner.clone());
|
||||
pointer.quick_assign(move |pointer, request, _data| {
|
||||
match request {
|
||||
|
@ -606,14 +595,9 @@ where
|
|||
if focus.as_ref().same_client_as(&pointer.as_ref()) {
|
||||
match surface {
|
||||
Some(surface) => {
|
||||
let role_data = CursorImageRole {
|
||||
hotspot: (hotspot_x, hotspot_y),
|
||||
};
|
||||
// we gracefully tolerate the client to provide a surface that
|
||||
// already had the "CursorImage" role, as most clients will
|
||||
// always reuse the same surface (and they are right to do so!)
|
||||
if token.with_role_data(&surface, |data| *data = role_data).is_err()
|
||||
&& token.give_role_with(&surface, role_data).is_err()
|
||||
// tolerate re-using the same surface
|
||||
if compositor::give_role(&surface, CURSOR_IMAGE_ROLE).is_err()
|
||||
&& compositor::get_role(&surface) != Some(CURSOR_IMAGE_ROLE)
|
||||
{
|
||||
pointer.as_ref().post_error(
|
||||
wl_pointer::Error::Role as u32,
|
||||
|
@ -621,6 +605,20 @@ where
|
|||
);
|
||||
return;
|
||||
}
|
||||
compositor::with_states(&surface, |states| {
|
||||
states.data_map.insert_if_missing_threadsafe(|| {
|
||||
Mutex::new(CursorImageAttributes { hotspot: (0, 0) })
|
||||
});
|
||||
states
|
||||
.data_map
|
||||
.get::<Mutex<CursorImageAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.hotspot = (hotspot_x, hotspot_y);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
image_callback(CursorImageStatus::Image(surface));
|
||||
}
|
||||
None => {
|
||||
|
|
|
@ -23,37 +23,18 @@
|
|||
//!
|
||||
//! ### Initialization
|
||||
//!
|
||||
//! To initialize this handler, simple use the [`wl_shell_init`]
|
||||
//! function provided in this module. You will need to provide it the [`CompositorToken`](crate::wayland::compositor::CompositorToken)
|
||||
//! you retrieved from an instantiation of the compositor handler provided by smithay.
|
||||
//! To initialize this handler, simple use the [`wl_shell_init`] function provided in this module.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # extern crate wayland_server;
|
||||
//! # #[macro_use] extern crate smithay;
|
||||
//! # extern crate wayland_protocols;
|
||||
//! #
|
||||
//! use smithay::wayland::compositor::roles::*;
|
||||
//! use smithay::wayland::compositor::CompositorToken;
|
||||
//! use smithay::wayland::shell::legacy::{wl_shell_init, ShellSurfaceRole, ShellRequest};
|
||||
//! # use wayland_server::protocol::{wl_seat, wl_output};
|
||||
//!
|
||||
//! // define the roles type. You need to integrate the XdgSurface role:
|
||||
//! define_roles!(MyRoles =>
|
||||
//! [ShellSurface, ShellSurfaceRole]
|
||||
//! );
|
||||
//! use smithay::wayland::shell::legacy::{wl_shell_init, ShellRequest};
|
||||
//!
|
||||
//! # let mut display = wayland_server::Display::new();
|
||||
//! # let (compositor_token, _, _) = smithay::wayland::compositor::compositor_init::<MyRoles, _, _>(
|
||||
//! # &mut display,
|
||||
//! # |_, _, _, _| {},
|
||||
//! # None
|
||||
//! # );
|
||||
//! let (shell_state, _) = wl_shell_init(
|
||||
//! &mut display,
|
||||
//! // token from the compositor implementation
|
||||
//! compositor_token,
|
||||
//! // your implementation
|
||||
//! |event: ShellRequest<_>| { /* ... */ },
|
||||
//! |event: ShellRequest| { /* ... */ },
|
||||
//! None // put a logger if you want
|
||||
//! );
|
||||
//!
|
||||
|
@ -66,8 +47,7 @@ use std::{
|
|||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use crate::wayland::compositor::{roles::Role, CompositorToken};
|
||||
use crate::wayland::Serial;
|
||||
use crate::wayland::{compositor, Serial};
|
||||
|
||||
use wayland_server::{
|
||||
protocol::{wl_output, wl_seat, wl_shell, wl_shell_surface, wl_surface},
|
||||
|
@ -80,7 +60,7 @@ mod wl_handlers;
|
|||
|
||||
/// Metadata associated with the `wl_surface` role
|
||||
#[derive(Debug)]
|
||||
pub struct ShellSurfaceRole {
|
||||
pub struct ShellSurfaceAttributes {
|
||||
/// Title of the surface
|
||||
pub title: String,
|
||||
/// Class of the surface
|
||||
|
@ -89,28 +69,13 @@ pub struct ShellSurfaceRole {
|
|||
}
|
||||
|
||||
/// A handle to a shell surface
|
||||
#[derive(Debug)]
|
||||
pub struct ShellSurface<R> {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ShellSurface {
|
||||
wl_surface: wl_surface::WlSurface,
|
||||
shell_surface: wl_shell_surface::WlShellSurface,
|
||||
token: CompositorToken<R>,
|
||||
}
|
||||
|
||||
// We implement Clone manually because #[derive(..)] would require R: Clone.
|
||||
impl<R> Clone for ShellSurface<R> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
wl_surface: self.wl_surface.clone(),
|
||||
shell_surface: self.shell_surface.clone(),
|
||||
token: self.token,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> ShellSurface<R>
|
||||
where
|
||||
R: Role<ShellSurfaceRole> + 'static,
|
||||
{
|
||||
impl ShellSurface {
|
||||
/// Is the shell surface referred by this handle still alive?
|
||||
pub fn alive(&self) -> bool {
|
||||
self.shell_surface.as_ref().is_alive() && self.wl_surface.as_ref().is_alive()
|
||||
|
@ -145,8 +110,13 @@ where
|
|||
if !self.alive() {
|
||||
return Err(PingError::DeadSurface);
|
||||
}
|
||||
self.token
|
||||
.with_role_data(&self.wl_surface, |data| {
|
||||
compositor::with_states(&self.wl_surface, |states| {
|
||||
let mut data = states
|
||||
.data_map
|
||||
.get::<Mutex<ShellSurfaceAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
if let Some(pending_ping) = data.pending_ping {
|
||||
return Err(PingError::PingAlreadyPending(pending_ping));
|
||||
}
|
||||
|
@ -225,13 +195,13 @@ pub enum ShellSurfaceKind {
|
|||
|
||||
/// A request triggered by a `wl_shell_surface`
|
||||
#[derive(Debug)]
|
||||
pub enum ShellRequest<R> {
|
||||
pub enum ShellRequest {
|
||||
/// A new shell surface was created
|
||||
///
|
||||
/// by default it has no kind and this should not be displayed
|
||||
NewShellSurface {
|
||||
/// The created surface
|
||||
surface: ShellSurface<R>,
|
||||
surface: ShellSurface,
|
||||
},
|
||||
/// A pong event
|
||||
///
|
||||
|
@ -239,14 +209,14 @@ pub enum ShellRequest<R> {
|
|||
/// event, smithay has already checked that the responded serial was valid.
|
||||
Pong {
|
||||
/// The surface that sent the pong
|
||||
surface: ShellSurface<R>,
|
||||
surface: ShellSurface,
|
||||
},
|
||||
/// Start of an interactive move
|
||||
///
|
||||
/// The surface requests that an interactive move is started on it
|
||||
Move {
|
||||
/// The surface requesting the move
|
||||
surface: ShellSurface<R>,
|
||||
surface: ShellSurface,
|
||||
/// Serial of the implicit grab that initiated the move
|
||||
serial: Serial,
|
||||
/// Seat associated with the move
|
||||
|
@ -257,7 +227,7 @@ pub enum ShellRequest<R> {
|
|||
/// The surface requests that an interactive resize is started on it
|
||||
Resize {
|
||||
/// The surface requesting the resize
|
||||
surface: ShellSurface<R>,
|
||||
surface: ShellSurface,
|
||||
/// Serial of the implicit grab that initiated the resize
|
||||
serial: Serial,
|
||||
/// Seat associated with the resize
|
||||
|
@ -268,7 +238,7 @@ pub enum ShellRequest<R> {
|
|||
/// The surface changed its kind
|
||||
SetKind {
|
||||
/// The surface
|
||||
surface: ShellSurface<R>,
|
||||
surface: ShellSurface,
|
||||
/// Its new kind
|
||||
kind: ShellSurfaceKind,
|
||||
},
|
||||
|
@ -279,36 +249,31 @@ pub enum ShellRequest<R> {
|
|||
/// This state allows you to retrieve a list of surfaces
|
||||
/// currently known to the shell global.
|
||||
#[derive(Debug)]
|
||||
pub struct ShellState<R> {
|
||||
known_surfaces: Vec<ShellSurface<R>>,
|
||||
pub struct ShellState {
|
||||
known_surfaces: Vec<ShellSurface>,
|
||||
}
|
||||
|
||||
impl<R> ShellState<R>
|
||||
where
|
||||
R: Role<ShellSurfaceRole> + 'static,
|
||||
{
|
||||
impl ShellState {
|
||||
/// Cleans the internal surface storage by removing all dead surfaces
|
||||
pub(crate) fn cleanup_surfaces(&mut self) {
|
||||
self.known_surfaces.retain(|s| s.alive());
|
||||
}
|
||||
|
||||
/// Access all the shell surfaces known by this handler
|
||||
pub fn surfaces(&self) -> &[ShellSurface<R>] {
|
||||
pub fn surfaces(&self) -> &[ShellSurface] {
|
||||
&self.known_surfaces[..]
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `wl_shell` global
|
||||
pub fn wl_shell_init<R, L, Impl>(
|
||||
pub fn wl_shell_init<L, Impl>(
|
||||
display: &mut Display,
|
||||
ctoken: CompositorToken<R>,
|
||||
implementation: Impl,
|
||||
logger: L,
|
||||
) -> (Arc<Mutex<ShellState<R>>>, Global<wl_shell::WlShell>)
|
||||
) -> (Arc<Mutex<ShellState>>, Global<wl_shell::WlShell>)
|
||||
where
|
||||
R: Role<ShellSurfaceRole> + 'static,
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
Impl: FnMut(ShellRequest<R>) + 'static,
|
||||
Impl: FnMut(ShellRequest) + 'static,
|
||||
{
|
||||
let _log = crate::slog_or_fallback(logger);
|
||||
|
||||
|
@ -322,7 +287,7 @@ where
|
|||
let global = display.create_global(
|
||||
1,
|
||||
Filter::new(move |(shell, _version), _, _data| {
|
||||
self::wl_handlers::implement_shell(shell, ctoken, implementation.clone(), state2.clone());
|
||||
self::wl_handlers::implement_shell(shell, implementation.clone(), state2.clone());
|
||||
}),
|
||||
);
|
||||
|
||||
|
|
|
@ -10,116 +10,116 @@ use wayland_server::{
|
|||
Filter, Main,
|
||||
};
|
||||
|
||||
use crate::wayland::compositor::{roles::Role, CompositorToken};
|
||||
static WL_SHELL_SURFACE_ROLE: &str = "wl_shell_surface";
|
||||
|
||||
use crate::wayland::compositor;
|
||||
use crate::wayland::Serial;
|
||||
|
||||
use super::{ShellRequest, ShellState, ShellSurface, ShellSurfaceKind, ShellSurfaceRole};
|
||||
use super::{ShellRequest, ShellState, ShellSurface, ShellSurfaceAttributes, ShellSurfaceKind};
|
||||
|
||||
pub(crate) fn implement_shell<R, Impl>(
|
||||
pub(crate) fn implement_shell<Impl>(
|
||||
shell: Main<wl_shell::WlShell>,
|
||||
ctoken: CompositorToken<R>,
|
||||
implementation: Rc<RefCell<Impl>>,
|
||||
state: Arc<Mutex<ShellState<R>>>,
|
||||
state: Arc<Mutex<ShellState>>,
|
||||
) where
|
||||
R: Role<ShellSurfaceRole> + 'static,
|
||||
Impl: FnMut(ShellRequest<R>) + 'static,
|
||||
Impl: FnMut(ShellRequest) + 'static,
|
||||
{
|
||||
shell.quick_assign(move |shell, req, _data| {
|
||||
let (id, surface) = match req {
|
||||
wl_shell::Request::GetShellSurface { id, surface } => (id, surface),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let role_data = ShellSurfaceRole {
|
||||
title: "".into(),
|
||||
class: "".into(),
|
||||
pending_ping: None,
|
||||
};
|
||||
if ctoken.give_role_with(&surface, role_data).is_err() {
|
||||
if compositor::give_role(&surface, WL_SHELL_SURFACE_ROLE).is_err() {
|
||||
shell
|
||||
.as_ref()
|
||||
.post_error(wl_shell::Error::Role as u32, "Surface already has a role.".into());
|
||||
return;
|
||||
}
|
||||
let shell_surface =
|
||||
implement_shell_surface(id, surface, implementation.clone(), ctoken, state.clone());
|
||||
compositor::with_states(&surface, |states| {
|
||||
states.data_map.insert_if_missing(|| {
|
||||
Mutex::new(ShellSurfaceAttributes {
|
||||
title: "".into(),
|
||||
class: "".into(),
|
||||
pending_ping: None,
|
||||
})
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
let shell_surface = implement_shell_surface(id, surface, implementation.clone(), state.clone());
|
||||
state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.known_surfaces
|
||||
.push(make_handle(&shell_surface, ctoken));
|
||||
.push(make_handle(&shell_surface));
|
||||
let mut imp = implementation.borrow_mut();
|
||||
(&mut *imp)(ShellRequest::NewShellSurface {
|
||||
surface: make_handle(&shell_surface, ctoken),
|
||||
surface: make_handle(&shell_surface),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn make_handle<R>(
|
||||
shell_surface: &wl_shell_surface::WlShellSurface,
|
||||
token: CompositorToken<R>,
|
||||
) -> ShellSurface<R>
|
||||
where
|
||||
R: Role<ShellSurfaceRole> + 'static,
|
||||
{
|
||||
fn make_handle(shell_surface: &wl_shell_surface::WlShellSurface) -> ShellSurface {
|
||||
let data = shell_surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
ShellSurface {
|
||||
wl_surface: data.surface.clone(),
|
||||
shell_surface: shell_surface.clone(),
|
||||
token,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ShellSurfaceUserData<R> {
|
||||
pub(crate) struct ShellSurfaceUserData {
|
||||
surface: wl_surface::WlSurface,
|
||||
state: Arc<Mutex<ShellState<R>>>,
|
||||
state: Arc<Mutex<ShellState>>,
|
||||
}
|
||||
|
||||
fn implement_shell_surface<R, Impl>(
|
||||
fn implement_shell_surface<Impl>(
|
||||
shell_surface: Main<wl_shell_surface::WlShellSurface>,
|
||||
surface: wl_surface::WlSurface,
|
||||
implementation: Rc<RefCell<Impl>>,
|
||||
ctoken: CompositorToken<R>,
|
||||
state: Arc<Mutex<ShellState<R>>>,
|
||||
state: Arc<Mutex<ShellState>>,
|
||||
) -> wl_shell_surface::WlShellSurface
|
||||
where
|
||||
R: Role<ShellSurfaceRole> + 'static,
|
||||
Impl: FnMut(ShellRequest<R>) + 'static,
|
||||
Impl: FnMut(ShellRequest) + 'static,
|
||||
{
|
||||
use self::wl_shell_surface::Request;
|
||||
shell_surface.quick_assign(move |shell_surface, req, _data| {
|
||||
let data = shell_surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
let mut user_impl = implementation.borrow_mut();
|
||||
match req {
|
||||
Request::Pong { serial } => {
|
||||
let serial = Serial::from(serial);
|
||||
let valid = ctoken
|
||||
.with_role_data(&data.surface, |data| {
|
||||
if data.pending_ping == Some(serial) {
|
||||
data.pending_ping = None;
|
||||
let valid = compositor::with_states(&data.surface, |states| {
|
||||
let mut guard = states
|
||||
.data_map
|
||||
.get::<Mutex<ShellSurfaceAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
if guard.pending_ping == Some(serial) {
|
||||
guard.pending_ping = None;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.expect("wl_shell_surface exists but surface has not the right role?");
|
||||
.unwrap();
|
||||
if valid {
|
||||
(&mut *user_impl)(ShellRequest::Pong {
|
||||
surface: make_handle(&shell_surface, ctoken),
|
||||
surface: make_handle(&shell_surface),
|
||||
});
|
||||
}
|
||||
}
|
||||
Request::Move { seat, serial } => {
|
||||
let serial = Serial::from(serial);
|
||||
(&mut *user_impl)(ShellRequest::Move {
|
||||
surface: make_handle(&shell_surface, ctoken),
|
||||
surface: make_handle(&shell_surface),
|
||||
serial,
|
||||
seat,
|
||||
})
|
||||
|
@ -127,18 +127,18 @@ where
|
|||
Request::Resize { seat, serial, edges } => {
|
||||
let serial = Serial::from(serial);
|
||||
(&mut *user_impl)(ShellRequest::Resize {
|
||||
surface: make_handle(&shell_surface, ctoken),
|
||||
surface: make_handle(&shell_surface),
|
||||
serial,
|
||||
seat,
|
||||
edges,
|
||||
})
|
||||
}
|
||||
Request::SetToplevel => (&mut *user_impl)(ShellRequest::SetKind {
|
||||
surface: make_handle(&shell_surface, ctoken),
|
||||
surface: make_handle(&shell_surface),
|
||||
kind: ShellSurfaceKind::Toplevel,
|
||||
}),
|
||||
Request::SetTransient { parent, x, y, flags } => (&mut *user_impl)(ShellRequest::SetKind {
|
||||
surface: make_handle(&shell_surface, ctoken),
|
||||
surface: make_handle(&shell_surface),
|
||||
kind: ShellSurfaceKind::Transient {
|
||||
parent,
|
||||
location: (x, y),
|
||||
|
@ -150,7 +150,7 @@ where
|
|||
framerate,
|
||||
output,
|
||||
} => (&mut *user_impl)(ShellRequest::SetKind {
|
||||
surface: make_handle(&shell_surface, ctoken),
|
||||
surface: make_handle(&shell_surface),
|
||||
kind: ShellSurfaceKind::Fullscreen {
|
||||
method,
|
||||
framerate,
|
||||
|
@ -167,7 +167,7 @@ where
|
|||
} => {
|
||||
let serial = Serial::from(serial);
|
||||
(&mut *user_impl)(ShellRequest::SetKind {
|
||||
surface: make_handle(&shell_surface, ctoken),
|
||||
surface: make_handle(&shell_surface),
|
||||
kind: ShellSurfaceKind::Popup {
|
||||
parent,
|
||||
serial,
|
||||
|
@ -178,18 +178,32 @@ where
|
|||
})
|
||||
}
|
||||
Request::SetMaximized { output } => (&mut *user_impl)(ShellRequest::SetKind {
|
||||
surface: make_handle(&shell_surface, ctoken),
|
||||
surface: make_handle(&shell_surface),
|
||||
kind: ShellSurfaceKind::Maximized { output },
|
||||
}),
|
||||
Request::SetTitle { title } => {
|
||||
ctoken
|
||||
.with_role_data(&data.surface, |data| data.title = title)
|
||||
.expect("wl_shell_surface exists but surface has not shell_surface role?!");
|
||||
compositor::with_states(&data.surface, |states| {
|
||||
let mut guard = states
|
||||
.data_map
|
||||
.get::<Mutex<ShellSurfaceAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
guard.title = title;
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
Request::SetClass { class_ } => {
|
||||
ctoken
|
||||
.with_role_data(&data.surface, |data| data.class = class_)
|
||||
.expect("wl_shell_surface exists but surface has not shell_surface role?!");
|
||||
compositor::with_states(&data.surface, |states| {
|
||||
let mut guard = states
|
||||
.data_map
|
||||
.get::<Mutex<ShellSurfaceAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
guard.class = class_;
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -200,7 +214,7 @@ where
|
|||
let data = shell_surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
data.state.lock().unwrap().cleanup_surfaces();
|
||||
},
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,8 @@
|
|||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
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::compositor;
|
||||
use crate::wayland::shell::xdg::PopupState;
|
||||
use crate::wayland::Serial;
|
||||
use wayland_protocols::xdg_shell::server::{
|
||||
xdg_popup, xdg_positioner, xdg_surface, xdg_toplevel, xdg_wm_base,
|
||||
|
@ -12,25 +13,25 @@ use crate::utils::Rectangle;
|
|||
|
||||
use super::{
|
||||
make_shell_client_data, PopupConfigure, PopupKind, PositionerState, ShellClient, ShellClientData,
|
||||
ShellData, ToplevelClientPending, ToplevelConfigure, ToplevelKind, XdgPopupSurfaceRole,
|
||||
XdgPopupSurfaceRoleAttributes, XdgRequest, XdgToplevelSurfaceRole, XdgToplevelSurfaceRoleAttributes,
|
||||
ShellData, SurfaceCachedState, ToplevelConfigure, ToplevelKind, XdgPopupSurfaceRoleAttributes,
|
||||
XdgRequest, XdgToplevelSurfaceRoleAttributes,
|
||||
};
|
||||
|
||||
pub(crate) fn implement_wm_base<R>(
|
||||
static XDG_TOPLEVEL_ROLE: &str = "xdg_toplevel";
|
||||
static XDG_POPUP_ROLE: &str = "xdg_toplevel";
|
||||
|
||||
pub(crate) fn implement_wm_base(
|
||||
shell: Main<xdg_wm_base::XdgWmBase>,
|
||||
shell_data: &ShellData<R>,
|
||||
) -> xdg_wm_base::XdgWmBase
|
||||
where
|
||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
shell.quick_assign(|shell, req, _data| wm_implementation::<R>(req, shell.deref().clone()));
|
||||
shell_data: &ShellData,
|
||||
) -> xdg_wm_base::XdgWmBase {
|
||||
shell.quick_assign(|shell, req, _data| wm_implementation(req, shell.deref().clone()));
|
||||
shell.as_ref().user_data().set(|| ShellUserData {
|
||||
shell_data: shell_data.clone(),
|
||||
client_data: Mutex::new(make_shell_client_data()),
|
||||
});
|
||||
let mut user_impl = shell_data.user_impl.borrow_mut();
|
||||
(&mut *user_impl)(XdgRequest::NewClient {
|
||||
client: make_shell_client(&shell, shell_data.compositor_token),
|
||||
client: make_shell_client(&shell),
|
||||
});
|
||||
shell.deref().clone()
|
||||
}
|
||||
|
@ -39,26 +40,19 @@ where
|
|||
* xdg_shell
|
||||
*/
|
||||
|
||||
pub(crate) struct ShellUserData<R> {
|
||||
shell_data: ShellData<R>,
|
||||
pub(crate) struct ShellUserData {
|
||||
shell_data: ShellData,
|
||||
pub(crate) client_data: Mutex<ShellClientData>,
|
||||
}
|
||||
|
||||
pub(crate) fn make_shell_client<R>(
|
||||
resource: &xdg_wm_base::XdgWmBase,
|
||||
token: CompositorToken<R>,
|
||||
) -> ShellClient<R> {
|
||||
pub(crate) fn make_shell_client(resource: &xdg_wm_base::XdgWmBase) -> ShellClient {
|
||||
ShellClient {
|
||||
kind: super::ShellClientKind::Xdg(resource.clone()),
|
||||
_token: token,
|
||||
}
|
||||
}
|
||||
|
||||
fn wm_implementation<R>(request: xdg_wm_base::Request, shell: xdg_wm_base::XdgWmBase)
|
||||
where
|
||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = shell.as_ref().user_data().get::<ShellUserData<R>>().unwrap();
|
||||
fn wm_implementation(request: xdg_wm_base::Request, shell: xdg_wm_base::XdgWmBase) {
|
||||
let data = shell.as_ref().user_data().get::<ShellUserData>().unwrap();
|
||||
match request {
|
||||
xdg_wm_base::Request::Destroy => {
|
||||
// all is handled by destructor
|
||||
|
@ -70,14 +64,13 @@ where
|
|||
// 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())
|
||||
});
|
||||
id.assign_destructor(Filter::new(|surface, _, _data| destroy_surface::<R>(surface)));
|
||||
id.quick_assign(|surface, req, _data| xdg_surface_implementation(req, surface.deref().clone()));
|
||||
id.assign_destructor(Filter::new(|surface, _, _data| destroy_surface(surface)));
|
||||
id.as_ref().user_data().set(|| XdgSurfaceUserData {
|
||||
shell_data: data.shell_data.clone(),
|
||||
wl_surface: surface,
|
||||
wm_base: shell.clone(),
|
||||
has_active_role: AtomicBool::new(false),
|
||||
});
|
||||
}
|
||||
xdg_wm_base::Request::Pong { serial } => {
|
||||
|
@ -94,7 +87,7 @@ where
|
|||
if valid {
|
||||
let mut user_impl = data.shell_data.user_impl.borrow_mut();
|
||||
(&mut *user_impl)(XdgRequest::ClientPong {
|
||||
client: make_shell_client(&shell, data.shell_data.compositor_token),
|
||||
client: make_shell_client(&shell),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -169,21 +162,15 @@ fn implement_positioner(positioner: Main<xdg_positioner::XdgPositioner>) -> xdg_
|
|||
* xdg_surface
|
||||
*/
|
||||
|
||||
struct XdgSurfaceUserData<R> {
|
||||
shell_data: ShellData<R>,
|
||||
struct XdgSurfaceUserData {
|
||||
shell_data: ShellData,
|
||||
wl_surface: wl_surface::WlSurface,
|
||||
wm_base: xdg_wm_base::XdgWmBase,
|
||||
has_active_role: AtomicBool,
|
||||
}
|
||||
|
||||
fn destroy_surface<R>(surface: xdg_surface::XdgSurface)
|
||||
where
|
||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<XdgSurfaceUserData<R>>()
|
||||
.unwrap();
|
||||
fn destroy_surface(surface: xdg_surface::XdgSurface) {
|
||||
let data = surface.as_ref().user_data().get::<XdgSurfaceUserData>().unwrap();
|
||||
if !data.wl_surface.as_ref().is_alive() {
|
||||
// the wl_surface is destroyed, this means the client is not
|
||||
// trying to change the role but it's a cleanup (possibly a
|
||||
|
@ -191,25 +178,12 @@ where
|
|||
return;
|
||||
}
|
||||
|
||||
if !data.shell_data.compositor_token.has_a_role(&data.wl_surface) {
|
||||
if compositor::get_role(&data.wl_surface).is_none() {
|
||||
// No role assigned to the surface, we can exit early.
|
||||
return;
|
||||
}
|
||||
|
||||
let has_active_xdg_role = data
|
||||
.shell_data
|
||||
.compositor_token
|
||||
.with_role_data(&data.wl_surface, |role: &mut XdgToplevelSurfaceRole| {
|
||||
role.is_some()
|
||||
})
|
||||
.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 {
|
||||
if data.has_active_role.load(Ordering::Acquire) {
|
||||
data.wm_base.as_ref().post_error(
|
||||
xdg_wm_base::Error::Role as u32,
|
||||
"xdg_surface was destroyed before its role object".into(),
|
||||
|
@ -217,14 +191,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn xdg_surface_implementation<R>(request: xdg_surface::Request, xdg_surface: xdg_surface::XdgSurface)
|
||||
where
|
||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
fn xdg_surface_implementation(request: xdg_surface::Request, xdg_surface: xdg_surface::XdgSurface) {
|
||||
let data = xdg_surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<XdgSurfaceUserData<R>>()
|
||||
.get::<XdgSurfaceUserData>()
|
||||
.unwrap();
|
||||
match request {
|
||||
xdg_surface::Request::Destroy => {
|
||||
|
@ -235,14 +206,7 @@ where
|
|||
let surface = &data.wl_surface;
|
||||
let shell = &data.wm_base;
|
||||
|
||||
let role_data = XdgToplevelSurfaceRole::Some(Default::default());
|
||||
|
||||
if data
|
||||
.shell_data
|
||||
.compositor_token
|
||||
.give_role_with(&surface, role_data)
|
||||
.is_err()
|
||||
{
|
||||
if compositor::give_role(surface, XDG_TOPLEVEL_ROLE).is_err() {
|
||||
shell.as_ref().post_error(
|
||||
xdg_wm_base::Error::Role as u32,
|
||||
"Surface already has a role.".into(),
|
||||
|
@ -250,10 +214,19 @@ where
|
|||
return;
|
||||
}
|
||||
|
||||
id.quick_assign(|toplevel, req, _data| {
|
||||
toplevel_implementation::<R>(req, toplevel.deref().clone())
|
||||
});
|
||||
id.assign_destructor(Filter::new(|toplevel, _, _data| destroy_toplevel::<R>(toplevel)));
|
||||
data.has_active_role.store(true, Ordering::Release);
|
||||
|
||||
compositor::with_states(surface, |states| {
|
||||
states
|
||||
.data_map
|
||||
.insert_if_missing_threadsafe(|| Mutex::new(XdgToplevelSurfaceRoleAttributes::default()))
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
compositor::add_commit_hook(surface, super::ToplevelSurface::commit_hook);
|
||||
|
||||
id.quick_assign(|toplevel, req, _data| toplevel_implementation(req, toplevel.deref().clone()));
|
||||
id.assign_destructor(Filter::new(|toplevel, _, _data| destroy_toplevel(toplevel)));
|
||||
id.as_ref().user_data().set(|| ShellSurfaceUserData {
|
||||
shell_data: data.shell_data.clone(),
|
||||
wl_surface: data.wl_surface.clone(),
|
||||
|
@ -286,11 +259,7 @@ where
|
|||
.clone();
|
||||
|
||||
let parent_surface = parent.map(|parent| {
|
||||
let parent_data = parent
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<XdgSurfaceUserData<R>>()
|
||||
.unwrap();
|
||||
let parent_data = parent.as_ref().user_data().get::<XdgSurfaceUserData>().unwrap();
|
||||
parent_data.wl_surface.clone()
|
||||
});
|
||||
|
||||
|
@ -298,21 +267,15 @@ where
|
|||
let surface = &data.wl_surface;
|
||||
let shell = &data.wm_base;
|
||||
|
||||
let role_data = XdgPopupSurfaceRole::Some(XdgPopupSurfaceRoleAttributes {
|
||||
let attributes = 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
|
||||
.give_role_with(&surface, role_data)
|
||||
.is_err()
|
||||
{
|
||||
};
|
||||
if compositor::give_role(surface, XDG_POPUP_ROLE).is_err() {
|
||||
shell.as_ref().post_error(
|
||||
xdg_wm_base::Error::Role as u32,
|
||||
"Surface already has a role.".into(),
|
||||
|
@ -320,8 +283,25 @@ where
|
|||
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)));
|
||||
data.has_active_role.store(true, Ordering::Release);
|
||||
|
||||
compositor::with_states(surface, |states| {
|
||||
states
|
||||
.data_map
|
||||
.insert_if_missing_threadsafe(|| Mutex::new(XdgPopupSurfaceRoleAttributes::default()));
|
||||
*states
|
||||
.data_map
|
||||
.get::<Mutex<XdgPopupSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap() = attributes;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
compositor::add_commit_hook(surface, super::PopupSurface::commit_hook);
|
||||
|
||||
id.quick_assign(|popup, req, _data| xdg_popup_implementation(req, popup.deref().clone()));
|
||||
id.assign_destructor(Filter::new(|popup, _, _data| destroy_popup(popup)));
|
||||
id.as_ref().user_data().set(|| ShellSurfaceUserData {
|
||||
shell_data: data.shell_data.clone(),
|
||||
wl_surface: data.wl_surface.clone(),
|
||||
|
@ -346,33 +326,28 @@ where
|
|||
// 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(
|
||||
let role = compositor::get_role(surface);
|
||||
|
||||
if role.is_none() {
|
||||
xdg_surface.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_xdg_role(surface, |role| {
|
||||
role.set_window_geometry(Rectangle { x, y, width, height })
|
||||
})
|
||||
.is_err();
|
||||
|
||||
if has_wrong_role {
|
||||
if role != Some(XDG_TOPLEVEL_ROLE) && role != Some(XDG_POPUP_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(),
|
||||
);
|
||||
}
|
||||
|
||||
compositor::with_states(surface, |states| {
|
||||
states.cached_state.pending::<SurfaceCachedState>().geometry =
|
||||
Some(Rectangle { x, y, width, height });
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
xdg_surface::Request::AckConfigure { serial } => {
|
||||
let serial = Serial::from(serial);
|
||||
|
@ -381,8 +356,8 @@ where
|
|||
// 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(
|
||||
if compositor::get_role(surface).is_none() {
|
||||
xdg_surface.as_ref().post_error(
|
||||
xdg_surface::Error::NotConstructed as u32,
|
||||
"xdg_surface must have a role.".into(),
|
||||
);
|
||||
|
@ -401,21 +376,38 @@ where
|
|||
// 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_xdg_role(surface, |role| role.ack_configure(serial))
|
||||
{
|
||||
Ok(Ok(configure)) => configure,
|
||||
Ok(Err(ConfigureError::SerialNotFound(serial))) => {
|
||||
let found_configure = compositor::with_states(surface, |states| {
|
||||
if states.role == Some(XDG_TOPLEVEL_ROLE) {
|
||||
Ok(states
|
||||
.data_map
|
||||
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.ack_configure(serial))
|
||||
} else if states.role == Some(XDG_POPUP_ROLE) {
|
||||
Ok(states
|
||||
.data_map
|
||||
.get::<Mutex<XdgPopupSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.ack_configure(serial))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
let configure = match found_configure {
|
||||
Ok(Some(configure)) => configure,
|
||||
Ok(None) => {
|
||||
data.wm_base.as_ref().post_error(
|
||||
xdg_wm_base::Error::InvalidSurfaceState as u32,
|
||||
format!("wrong configure serial: {}", <u32>::from(serial)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
Err(_) => {
|
||||
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(),
|
||||
|
@ -438,64 +430,54 @@ where
|
|||
* xdg_toplevel
|
||||
*/
|
||||
|
||||
pub(crate) struct ShellSurfaceUserData<R> {
|
||||
pub(crate) shell_data: ShellData<R>,
|
||||
pub(crate) struct ShellSurfaceUserData {
|
||||
pub(crate) shell_data: ShellData,
|
||||
pub(crate) wl_surface: wl_surface::WlSurface,
|
||||
pub(crate) wm_base: xdg_wm_base::XdgWmBase,
|
||||
pub(crate) xdg_surface: xdg_surface::XdgSurface,
|
||||
}
|
||||
|
||||
// Utility functions allowing to factor out a lot of the upcoming logic
|
||||
fn with_surface_toplevel_role_data<R, F, T>(
|
||||
shell_data: &ShellData<R>,
|
||||
toplevel: &xdg_toplevel::XdgToplevel,
|
||||
f: F,
|
||||
) -> T
|
||||
fn with_surface_toplevel_role_data<F, T>(toplevel: &xdg_toplevel::XdgToplevel, f: F) -> T
|
||||
where
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
F: FnOnce(&mut XdgToplevelSurfaceRoleAttributes) -> T,
|
||||
{
|
||||
let data = toplevel
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
shell_data
|
||||
.compositor_token
|
||||
.with_role_data::<XdgToplevelSurfaceRole, _, _>(&data.wl_surface, |role| {
|
||||
let attributes = role
|
||||
.as_mut()
|
||||
.expect("xdg_toplevel exists but role has been destroyed?!");
|
||||
f(attributes)
|
||||
compositor::with_states(&data.wl_surface, |states| {
|
||||
f(&mut *states
|
||||
.data_map
|
||||
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap())
|
||||
})
|
||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!")
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn with_surface_toplevel_client_pending<R, F, T>(
|
||||
shell_data: &ShellData<R>,
|
||||
toplevel: &xdg_toplevel::XdgToplevel,
|
||||
f: F,
|
||||
) -> T
|
||||
fn with_toplevel_pending_state<F, T>(toplevel: &xdg_toplevel::XdgToplevel, f: F) -> T
|
||||
where
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
F: FnOnce(&mut ToplevelClientPending) -> T,
|
||||
F: FnOnce(&mut SurfaceCachedState) -> 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())
|
||||
let data = toplevel
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
compositor::with_states(&data.wl_surface, |states| {
|
||||
f(&mut *states.cached_state.pending::<SurfaceCachedState>())
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn send_toplevel_configure<R>(resource: &xdg_toplevel::XdgToplevel, configure: ToplevelConfigure)
|
||||
where
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
{
|
||||
pub fn send_toplevel_configure(resource: &xdg_toplevel::XdgToplevel, configure: ToplevelConfigure) {
|
||||
let data = resource
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
|
||||
let (width, height) = configure.state.size.unwrap_or((0, 0));
|
||||
|
@ -518,27 +500,23 @@ where
|
|||
data.xdg_surface.configure(serial.into());
|
||||
}
|
||||
|
||||
fn make_toplevel_handle<R: 'static>(resource: &xdg_toplevel::XdgToplevel) -> super::ToplevelSurface<R> {
|
||||
fn make_toplevel_handle(resource: &xdg_toplevel::XdgToplevel) -> super::ToplevelSurface {
|
||||
let data = resource
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
super::ToplevelSurface {
|
||||
wl_surface: data.wl_surface.clone(),
|
||||
shell_surface: ToplevelKind::Xdg(resource.clone()),
|
||||
token: data.shell_data.compositor_token,
|
||||
}
|
||||
}
|
||||
|
||||
fn toplevel_implementation<R>(request: xdg_toplevel::Request, toplevel: xdg_toplevel::XdgToplevel)
|
||||
where
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
{
|
||||
fn toplevel_implementation(request: xdg_toplevel::Request, toplevel: xdg_toplevel::XdgToplevel) {
|
||||
let data = toplevel
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
match request {
|
||||
xdg_toplevel::Request::Destroy => {
|
||||
|
@ -546,12 +524,12 @@ where
|
|||
}
|
||||
xdg_toplevel::Request::SetParent { parent } => {
|
||||
// Parent is not double buffered, we can set it directly
|
||||
with_surface_toplevel_role_data(&data.shell_data, &toplevel, |data| {
|
||||
with_surface_toplevel_role_data(&toplevel, |data| {
|
||||
data.parent = parent.map(|toplevel_surface_parent| {
|
||||
toplevel_surface_parent
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap()
|
||||
.wl_surface
|
||||
.clone()
|
||||
|
@ -560,13 +538,13 @@ where
|
|||
}
|
||||
xdg_toplevel::Request::SetTitle { title } => {
|
||||
// Title is not double buffered, we can set it directly
|
||||
with_surface_toplevel_role_data(&data.shell_data, &toplevel, |data| {
|
||||
with_surface_toplevel_role_data(&toplevel, |data| {
|
||||
data.title = Some(title);
|
||||
});
|
||||
}
|
||||
xdg_toplevel::Request::SetAppId { app_id } => {
|
||||
// AppId is not double buffered, we can set it directly
|
||||
with_surface_toplevel_role_data(&data.shell_data, &toplevel, |role| {
|
||||
with_surface_toplevel_role_data(&toplevel, |role| {
|
||||
role.app_id = Some(app_id);
|
||||
});
|
||||
}
|
||||
|
@ -606,13 +584,13 @@ where
|
|||
});
|
||||
}
|
||||
xdg_toplevel::Request::SetMaxSize { width, height } => {
|
||||
with_surface_toplevel_client_pending(&data.shell_data, &toplevel, |toplevel_data| {
|
||||
toplevel_data.max_size = Some((width, height));
|
||||
with_toplevel_pending_state(&toplevel, |toplevel_data| {
|
||||
toplevel_data.max_size = (width, height);
|
||||
});
|
||||
}
|
||||
xdg_toplevel::Request::SetMinSize { width, height } => {
|
||||
with_surface_toplevel_client_pending(&data.shell_data, &toplevel, |toplevel_data| {
|
||||
toplevel_data.min_size = Some((width, height));
|
||||
with_toplevel_pending_state(&toplevel, |toplevel_data| {
|
||||
toplevel_data.min_size = (width, height);
|
||||
});
|
||||
}
|
||||
xdg_toplevel::Request::SetMaximized => {
|
||||
|
@ -649,26 +627,14 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn destroy_toplevel<R>(toplevel: xdg_toplevel::XdgToplevel)
|
||||
where
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
{
|
||||
fn destroy_toplevel(toplevel: xdg_toplevel::XdgToplevel) {
|
||||
let data = toplevel
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
if !data.wl_surface.as_ref().is_alive() {
|
||||
// the wl_surface is destroyed, this means the client is not
|
||||
// trying to change the role but it's a cleanup (possibly a
|
||||
// disconnecting client), ignore the protocol check.
|
||||
} else {
|
||||
data.shell_data
|
||||
.compositor_token
|
||||
.with_role_data(&data.wl_surface, |role_data| {
|
||||
*role_data = XdgToplevelSurfaceRole::None;
|
||||
})
|
||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
||||
if let Some(data) = data.xdg_surface.as_ref().user_data().get::<XdgSurfaceUserData>() {
|
||||
data.has_active_role.store(false, Ordering::Release);
|
||||
}
|
||||
// remove this surface from the known ones (as well as any leftover dead surface)
|
||||
data.shell_data
|
||||
|
@ -683,14 +649,11 @@ where
|
|||
* xdg_popup
|
||||
*/
|
||||
|
||||
pub(crate) fn send_popup_configure<R>(resource: &xdg_popup::XdgPopup, configure: PopupConfigure)
|
||||
where
|
||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
pub(crate) fn send_popup_configure(resource: &xdg_popup::XdgPopup, configure: PopupConfigure) {
|
||||
let data = resource
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
|
||||
let serial = configure.serial;
|
||||
|
@ -704,28 +667,20 @@ where
|
|||
data.xdg_surface.configure(serial.into());
|
||||
}
|
||||
|
||||
fn make_popup_handle<R: 'static>(resource: &xdg_popup::XdgPopup) -> super::PopupSurface<R> {
|
||||
fn make_popup_handle(resource: &xdg_popup::XdgPopup) -> super::PopupSurface {
|
||||
let data = resource
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
super::PopupSurface {
|
||||
wl_surface: data.wl_surface.clone(),
|
||||
shell_surface: PopupKind::Xdg(resource.clone()),
|
||||
token: data.shell_data.compositor_token,
|
||||
}
|
||||
}
|
||||
|
||||
fn xdg_popup_implementation<R>(request: xdg_popup::Request, popup: xdg_popup::XdgPopup)
|
||||
where
|
||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = popup
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.unwrap();
|
||||
fn xdg_popup_implementation(request: xdg_popup::Request, popup: xdg_popup::XdgPopup) {
|
||||
let data = popup.as_ref().user_data().get::<ShellSurfaceUserData>().unwrap();
|
||||
match request {
|
||||
xdg_popup::Request::Destroy => {
|
||||
// all is handled by our destructor
|
||||
|
@ -744,26 +699,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn destroy_popup<R>(popup: xdg_popup::XdgPopup)
|
||||
where
|
||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = popup
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.unwrap();
|
||||
if !data.wl_surface.as_ref().is_alive() {
|
||||
// the wl_surface is destroyed, this means the client is not
|
||||
// trying to change the role but it's a cleanup (possibly a
|
||||
// disconnecting client), ignore the protocol check.
|
||||
} else {
|
||||
data.shell_data
|
||||
.compositor_token
|
||||
.with_role_data(&data.wl_surface, |role_data| {
|
||||
*role_data = XdgPopupSurfaceRole::None;
|
||||
})
|
||||
.expect("xdg_popup exists but surface has not shell_surface role?!");
|
||||
fn destroy_popup(popup: xdg_popup::XdgPopup) {
|
||||
let data = popup.as_ref().user_data().get::<ShellSurfaceUserData>().unwrap();
|
||||
if let Some(data) = data.xdg_surface.as_ref().user_data().get::<XdgSurfaceUserData>() {
|
||||
data.has_active_role.store(false, Ordering::Release);
|
||||
}
|
||||
// remove this surface from the known ones (as well as any leftover dead surface)
|
||||
data.shell_data
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
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::compositor;
|
||||
use crate::wayland::shell::xdg::PopupState;
|
||||
use crate::wayland::Serial;
|
||||
use wayland_protocols::{
|
||||
unstable::xdg_shell::v6::server::{
|
||||
|
@ -15,25 +16,25 @@ use crate::utils::Rectangle;
|
|||
|
||||
use super::{
|
||||
make_shell_client_data, PopupConfigure, PopupKind, PositionerState, ShellClient, ShellClientData,
|
||||
ShellData, ToplevelClientPending, ToplevelConfigure, ToplevelKind, XdgPopupSurfaceRole,
|
||||
XdgPopupSurfaceRoleAttributes, XdgRequest, XdgToplevelSurfaceRole, XdgToplevelSurfaceRoleAttributes,
|
||||
ShellData, SurfaceCachedState, ToplevelConfigure, ToplevelKind, XdgPopupSurfaceRoleAttributes,
|
||||
XdgRequest, XdgToplevelSurfaceRoleAttributes,
|
||||
};
|
||||
|
||||
pub(crate) fn implement_shell<R>(
|
||||
static ZXDG_TOPLEVEL_ROLE: &str = "zxdg_toplevel";
|
||||
static ZXDG_POPUP_ROLE: &str = "zxdg_toplevel";
|
||||
|
||||
pub(crate) fn implement_shell(
|
||||
shell: Main<zxdg_shell_v6::ZxdgShellV6>,
|
||||
shell_data: &ShellData<R>,
|
||||
) -> zxdg_shell_v6::ZxdgShellV6
|
||||
where
|
||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
shell.quick_assign(|shell, req, _data| shell_implementation::<R>(req, shell.deref().clone()));
|
||||
shell_data: &ShellData,
|
||||
) -> zxdg_shell_v6::ZxdgShellV6 {
|
||||
shell.quick_assign(|shell, req, _data| shell_implementation(req, shell.deref().clone()));
|
||||
shell.as_ref().user_data().set(|| ShellUserData {
|
||||
shell_data: shell_data.clone(),
|
||||
client_data: Mutex::new(make_shell_client_data()),
|
||||
});
|
||||
let mut user_impl = shell_data.user_impl.borrow_mut();
|
||||
(&mut *user_impl)(XdgRequest::NewClient {
|
||||
client: make_shell_client(&shell, shell_data.compositor_token),
|
||||
client: make_shell_client(&shell),
|
||||
});
|
||||
shell.deref().clone()
|
||||
}
|
||||
|
@ -42,26 +43,19 @@ where
|
|||
* xdg_shell
|
||||
*/
|
||||
|
||||
pub(crate) struct ShellUserData<R> {
|
||||
shell_data: ShellData<R>,
|
||||
pub(crate) struct ShellUserData {
|
||||
shell_data: ShellData,
|
||||
pub(crate) client_data: Mutex<ShellClientData>,
|
||||
}
|
||||
|
||||
pub(crate) fn make_shell_client<R>(
|
||||
resource: &zxdg_shell_v6::ZxdgShellV6,
|
||||
token: CompositorToken<R>,
|
||||
) -> ShellClient<R> {
|
||||
pub(crate) fn make_shell_client(resource: &zxdg_shell_v6::ZxdgShellV6) -> ShellClient {
|
||||
ShellClient {
|
||||
kind: super::ShellClientKind::ZxdgV6(resource.clone()),
|
||||
_token: token,
|
||||
}
|
||||
}
|
||||
|
||||
fn shell_implementation<R>(request: zxdg_shell_v6::Request, shell: zxdg_shell_v6::ZxdgShellV6)
|
||||
where
|
||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = shell.as_ref().user_data().get::<ShellUserData<R>>().unwrap();
|
||||
fn shell_implementation(request: zxdg_shell_v6::Request, shell: zxdg_shell_v6::ZxdgShellV6) {
|
||||
let data = shell.as_ref().user_data().get::<ShellUserData>().unwrap();
|
||||
match request {
|
||||
zxdg_shell_v6::Request::Destroy => {
|
||||
// all is handled by destructor
|
||||
|
@ -70,14 +64,13 @@ where
|
|||
implement_positioner(id);
|
||||
}
|
||||
zxdg_shell_v6::Request::GetXdgSurface { id, surface } => {
|
||||
id.quick_assign(|surface, req, _data| {
|
||||
xdg_surface_implementation::<R>(req, surface.deref().clone())
|
||||
});
|
||||
id.assign_destructor(Filter::new(|surface, _, _data| destroy_surface::<R>(surface)));
|
||||
id.quick_assign(|surface, req, _data| xdg_surface_implementation(req, surface.deref().clone()));
|
||||
id.assign_destructor(Filter::new(|surface, _, _data| destroy_surface(surface)));
|
||||
id.as_ref().user_data().set(|| XdgSurfaceUserData {
|
||||
shell_data: data.shell_data.clone(),
|
||||
wl_surface: surface,
|
||||
shell: shell.clone(),
|
||||
has_active_role: AtomicBool::new(false),
|
||||
});
|
||||
}
|
||||
zxdg_shell_v6::Request::Pong { serial } => {
|
||||
|
@ -94,7 +87,7 @@ where
|
|||
if valid {
|
||||
let mut user_impl = data.shell_data.user_impl.borrow_mut();
|
||||
(&mut *user_impl)(XdgRequest::ClientPong {
|
||||
client: make_shell_client(&shell, data.shell_data.compositor_token),
|
||||
client: make_shell_client(&shell),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -185,21 +178,15 @@ fn implement_positioner(
|
|||
* xdg_surface
|
||||
*/
|
||||
|
||||
struct XdgSurfaceUserData<R> {
|
||||
shell_data: ShellData<R>,
|
||||
struct XdgSurfaceUserData {
|
||||
shell_data: ShellData,
|
||||
wl_surface: wl_surface::WlSurface,
|
||||
shell: zxdg_shell_v6::ZxdgShellV6,
|
||||
has_active_role: AtomicBool,
|
||||
}
|
||||
|
||||
fn destroy_surface<R>(surface: zxdg_surface_v6::ZxdgSurfaceV6)
|
||||
where
|
||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<XdgSurfaceUserData<R>>()
|
||||
.unwrap();
|
||||
fn destroy_surface(surface: zxdg_surface_v6::ZxdgSurfaceV6) {
|
||||
let data = surface.as_ref().user_data().get::<XdgSurfaceUserData>().unwrap();
|
||||
if !data.wl_surface.as_ref().is_alive() {
|
||||
// the wl_surface is destroyed, this means the client is not
|
||||
// trying to change the role but it's a cleanup (possibly a
|
||||
|
@ -207,25 +194,12 @@ where
|
|||
return;
|
||||
}
|
||||
|
||||
if !data.shell_data.compositor_token.has_a_role(&data.wl_surface) {
|
||||
if compositor::get_role(&data.wl_surface).is_none() {
|
||||
// No role assigned to the surface, we can exit early.
|
||||
return;
|
||||
}
|
||||
|
||||
let has_active_xdg_role = data
|
||||
.shell_data
|
||||
.compositor_token
|
||||
.with_role_data(&data.wl_surface, |role: &mut XdgToplevelSurfaceRole| {
|
||||
role.is_some()
|
||||
})
|
||||
.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 {
|
||||
if data.has_active_role.load(Ordering::Acquire) {
|
||||
data.shell.as_ref().post_error(
|
||||
zxdg_shell_v6::Error::Role as u32,
|
||||
"xdg_surface was destroyed before its role object".into(),
|
||||
|
@ -233,16 +207,14 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn xdg_surface_implementation<R>(
|
||||
fn xdg_surface_implementation(
|
||||
request: zxdg_surface_v6::Request,
|
||||
xdg_surface: zxdg_surface_v6::ZxdgSurfaceV6,
|
||||
) where
|
||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
) {
|
||||
let data = xdg_surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<XdgSurfaceUserData<R>>()
|
||||
.get::<XdgSurfaceUserData>()
|
||||
.unwrap();
|
||||
match request {
|
||||
zxdg_surface_v6::Request::Destroy => {
|
||||
|
@ -253,14 +225,7 @@ fn xdg_surface_implementation<R>(
|
|||
let surface = &data.wl_surface;
|
||||
let shell = &data.shell;
|
||||
|
||||
let role_data = XdgToplevelSurfaceRole::Some(Default::default());
|
||||
|
||||
if data
|
||||
.shell_data
|
||||
.compositor_token
|
||||
.give_role_with(&surface, role_data)
|
||||
.is_err()
|
||||
{
|
||||
if compositor::give_role(surface, ZXDG_TOPLEVEL_ROLE).is_err() {
|
||||
shell.as_ref().post_error(
|
||||
zxdg_shell_v6::Error::Role as u32,
|
||||
"Surface already has a role.".into(),
|
||||
|
@ -268,10 +233,19 @@ fn xdg_surface_implementation<R>(
|
|||
return;
|
||||
}
|
||||
|
||||
id.quick_assign(|toplevel, req, _data| {
|
||||
toplevel_implementation::<R>(req, toplevel.deref().clone())
|
||||
});
|
||||
id.assign_destructor(Filter::new(|toplevel, _, _data| destroy_toplevel::<R>(toplevel)));
|
||||
data.has_active_role.store(true, Ordering::Release);
|
||||
|
||||
compositor::with_states(surface, |states| {
|
||||
states
|
||||
.data_map
|
||||
.insert_if_missing_threadsafe(|| Mutex::new(XdgToplevelSurfaceRoleAttributes::default()))
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
compositor::add_commit_hook(surface, super::ToplevelSurface::commit_hook);
|
||||
|
||||
id.quick_assign(|toplevel, req, _data| toplevel_implementation(req, toplevel.deref().clone()));
|
||||
id.assign_destructor(Filter::new(|toplevel, _, _data| destroy_toplevel(toplevel)));
|
||||
id.as_ref().user_data().set(|| ShellSurfaceUserData {
|
||||
shell_data: data.shell_data.clone(),
|
||||
wl_surface: data.wl_surface.clone(),
|
||||
|
@ -304,11 +278,7 @@ fn xdg_surface_implementation<R>(
|
|||
.clone();
|
||||
|
||||
let parent_surface = {
|
||||
let parent_data = parent
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<XdgSurfaceUserData<R>>()
|
||||
.unwrap();
|
||||
let parent_data = parent.as_ref().user_data().get::<XdgSurfaceUserData>().unwrap();
|
||||
parent_data.wl_surface.clone()
|
||||
};
|
||||
|
||||
|
@ -316,21 +286,15 @@ fn xdg_surface_implementation<R>(
|
|||
let surface = &data.wl_surface;
|
||||
let shell = &data.shell;
|
||||
|
||||
let role_data = XdgPopupSurfaceRole::Some(XdgPopupSurfaceRoleAttributes {
|
||||
let attributes = 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
|
||||
.give_role_with(&surface, role_data)
|
||||
.is_err()
|
||||
{
|
||||
};
|
||||
if compositor::give_role(surface, ZXDG_POPUP_ROLE).is_err() {
|
||||
shell.as_ref().post_error(
|
||||
zxdg_shell_v6::Error::Role as u32,
|
||||
"Surface already has a role.".into(),
|
||||
|
@ -338,8 +302,25 @@ fn xdg_surface_implementation<R>(
|
|||
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)));
|
||||
data.has_active_role.store(true, Ordering::Release);
|
||||
|
||||
compositor::with_states(surface, |states| {
|
||||
states
|
||||
.data_map
|
||||
.insert_if_missing_threadsafe(|| Mutex::new(XdgPopupSurfaceRoleAttributes::default()));
|
||||
*states
|
||||
.data_map
|
||||
.get::<Mutex<XdgPopupSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap() = attributes;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
compositor::add_commit_hook(surface, super::PopupSurface::commit_hook);
|
||||
|
||||
id.quick_assign(|popup, req, _data| popup_implementation(req, popup.deref().clone()));
|
||||
id.assign_destructor(Filter::new(|popup, _, _data| destroy_popup(popup)));
|
||||
id.as_ref().user_data().set(|| ShellSurfaceUserData {
|
||||
shell_data: data.shell_data.clone(),
|
||||
wl_surface: data.wl_surface.clone(),
|
||||
|
@ -364,33 +345,28 @@ fn xdg_surface_implementation<R>(
|
|||
// 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(
|
||||
let role = compositor::get_role(surface);
|
||||
|
||||
if role.is_none() {
|
||||
xdg_surface.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_xdg_role(surface, |role| {
|
||||
role.set_window_geometry(Rectangle { x, y, width, height })
|
||||
})
|
||||
.is_err();
|
||||
|
||||
if has_wrong_role {
|
||||
if role != Some(ZXDG_TOPLEVEL_ROLE) && role != Some(ZXDG_POPUP_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(),
|
||||
);
|
||||
}
|
||||
|
||||
compositor::with_states(surface, |states| {
|
||||
states.cached_state.pending::<SurfaceCachedState>().geometry =
|
||||
Some(Rectangle { x, y, width, height });
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
zxdg_surface_v6::Request::AckConfigure { serial } => {
|
||||
let serial = Serial::from(serial);
|
||||
|
@ -399,7 +375,7 @@ fn xdg_surface_implementation<R>(
|
|||
// 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) {
|
||||
if compositor::get_role(surface).is_none() {
|
||||
data.shell.as_ref().post_error(
|
||||
zxdg_surface_v6::Error::NotConstructed as u32,
|
||||
"xdg_surface must have a role.".into(),
|
||||
|
@ -420,13 +396,31 @@ fn xdg_surface_implementation<R>(
|
|||
//
|
||||
// This can be used to integrate custom protocol extensions
|
||||
//
|
||||
let configure = match data
|
||||
.shell_data
|
||||
.compositor_token
|
||||
.with_xdg_role(surface, |role| role.ack_configure(serial))
|
||||
{
|
||||
Ok(Ok(configure)) => configure,
|
||||
Ok(Err(ConfigureError::SerialNotFound(serial))) => {
|
||||
let found_configure = compositor::with_states(surface, |states| {
|
||||
if states.role == Some(ZXDG_TOPLEVEL_ROLE) {
|
||||
Ok(states
|
||||
.data_map
|
||||
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.ack_configure(serial))
|
||||
} else if states.role == Some(ZXDG_POPUP_ROLE) {
|
||||
Ok(states
|
||||
.data_map
|
||||
.get::<Mutex<XdgPopupSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.ack_configure(serial))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
let configure = match found_configure {
|
||||
Ok(Some(configure)) => configure,
|
||||
Ok(None) => {
|
||||
data.shell.as_ref().post_error(
|
||||
zxdg_shell_v6::Error::InvalidSurfaceState as u32,
|
||||
format!("wrong configure serial: {}", <u32>::from(serial)),
|
||||
|
@ -456,64 +450,54 @@ fn xdg_surface_implementation<R>(
|
|||
* xdg_toplevel
|
||||
*/
|
||||
|
||||
pub struct ShellSurfaceUserData<R> {
|
||||
pub(crate) shell_data: ShellData<R>,
|
||||
pub struct ShellSurfaceUserData {
|
||||
pub(crate) shell_data: ShellData,
|
||||
pub(crate) wl_surface: wl_surface::WlSurface,
|
||||
pub(crate) shell: zxdg_shell_v6::ZxdgShellV6,
|
||||
pub(crate) xdg_surface: zxdg_surface_v6::ZxdgSurfaceV6,
|
||||
}
|
||||
|
||||
// Utility functions allowing to factor out a lot of the upcoming logic
|
||||
fn with_surface_toplevel_role_data<R, F, T>(
|
||||
shell_data: &ShellData<R>,
|
||||
toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6,
|
||||
f: F,
|
||||
) -> T
|
||||
fn with_surface_toplevel_role_data<F, T>(toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, f: F) -> T
|
||||
where
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
F: FnOnce(&mut XdgToplevelSurfaceRoleAttributes) -> T,
|
||||
{
|
||||
let data = toplevel
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
shell_data
|
||||
.compositor_token
|
||||
.with_role_data::<XdgToplevelSurfaceRole, _, _>(&data.wl_surface, |role| {
|
||||
let attributes = role
|
||||
.as_mut()
|
||||
.expect("xdg_toplevel exists but role has been destroyed?!");
|
||||
f(attributes)
|
||||
compositor::with_states(&data.wl_surface, |states| {
|
||||
f(&mut *states
|
||||
.data_map
|
||||
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap())
|
||||
})
|
||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!")
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn with_surface_toplevel_client_pending<R, F, T>(
|
||||
shell_data: &ShellData<R>,
|
||||
toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6,
|
||||
f: F,
|
||||
) -> T
|
||||
fn with_toplevel_pending_state<F, T>(toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, f: F) -> T
|
||||
where
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
F: FnOnce(&mut ToplevelClientPending) -> T,
|
||||
F: FnOnce(&mut SurfaceCachedState) -> 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())
|
||||
let data = toplevel
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
compositor::with_states(&data.wl_surface, |states| {
|
||||
f(&mut *states.cached_state.pending::<SurfaceCachedState>())
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn send_toplevel_configure<R>(resource: &zxdg_toplevel_v6::ZxdgToplevelV6, configure: ToplevelConfigure)
|
||||
where
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
{
|
||||
pub fn send_toplevel_configure(resource: &zxdg_toplevel_v6::ZxdgToplevelV6, configure: ToplevelConfigure) {
|
||||
let data = resource
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
|
||||
let (width, height) = configure.state.size.unwrap_or((0, 0));
|
||||
|
@ -536,41 +520,35 @@ where
|
|||
data.xdg_surface.configure(serial.into());
|
||||
}
|
||||
|
||||
fn make_toplevel_handle<R: 'static>(
|
||||
resource: &zxdg_toplevel_v6::ZxdgToplevelV6,
|
||||
) -> super::ToplevelSurface<R> {
|
||||
fn make_toplevel_handle(resource: &zxdg_toplevel_v6::ZxdgToplevelV6) -> super::ToplevelSurface {
|
||||
let data = resource
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
super::ToplevelSurface {
|
||||
wl_surface: data.wl_surface.clone(),
|
||||
shell_surface: ToplevelKind::ZxdgV6(resource.clone()),
|
||||
token: data.shell_data.compositor_token,
|
||||
}
|
||||
}
|
||||
|
||||
fn toplevel_implementation<R>(request: zxdg_toplevel_v6::Request, toplevel: zxdg_toplevel_v6::ZxdgToplevelV6)
|
||||
where
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
{
|
||||
fn toplevel_implementation(request: zxdg_toplevel_v6::Request, toplevel: zxdg_toplevel_v6::ZxdgToplevelV6) {
|
||||
let data = toplevel
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
match request {
|
||||
zxdg_toplevel_v6::Request::Destroy => {
|
||||
// all it done by the destructor
|
||||
}
|
||||
zxdg_toplevel_v6::Request::SetParent { parent } => {
|
||||
with_surface_toplevel_role_data(&data.shell_data, &toplevel, |data| {
|
||||
with_surface_toplevel_role_data(&toplevel, |data| {
|
||||
data.parent = parent.map(|toplevel_surface_parent| {
|
||||
let parent_data = toplevel_surface_parent
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
parent_data.wl_surface.clone()
|
||||
})
|
||||
|
@ -578,13 +556,13 @@ where
|
|||
}
|
||||
zxdg_toplevel_v6::Request::SetTitle { title } => {
|
||||
// Title is not double buffered, we can set it directly
|
||||
with_surface_toplevel_role_data(&data.shell_data, &toplevel, |data| {
|
||||
with_surface_toplevel_role_data(&toplevel, |data| {
|
||||
data.title = Some(title);
|
||||
});
|
||||
}
|
||||
zxdg_toplevel_v6::Request::SetAppId { app_id } => {
|
||||
// AppId is not double buffered, we can set it directly
|
||||
with_surface_toplevel_role_data(&data.shell_data, &toplevel, |role| {
|
||||
with_surface_toplevel_role_data(&toplevel, |role| {
|
||||
role.app_id = Some(app_id);
|
||||
});
|
||||
}
|
||||
|
@ -623,13 +601,13 @@ where
|
|||
});
|
||||
}
|
||||
zxdg_toplevel_v6::Request::SetMaxSize { width, height } => {
|
||||
with_surface_toplevel_client_pending(&data.shell_data, &toplevel, |toplevel_data| {
|
||||
toplevel_data.max_size = Some((width, height));
|
||||
with_toplevel_pending_state(&toplevel, |toplevel_data| {
|
||||
toplevel_data.max_size = (width, height);
|
||||
});
|
||||
}
|
||||
zxdg_toplevel_v6::Request::SetMinSize { width, height } => {
|
||||
with_surface_toplevel_client_pending(&data.shell_data, &toplevel, |toplevel_data| {
|
||||
toplevel_data.min_size = Some((width, height));
|
||||
with_toplevel_pending_state(&toplevel, |toplevel_data| {
|
||||
toplevel_data.min_size = (width, height);
|
||||
});
|
||||
}
|
||||
zxdg_toplevel_v6::Request::SetMaximized => {
|
||||
|
@ -666,26 +644,14 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn destroy_toplevel<R>(toplevel: zxdg_toplevel_v6::ZxdgToplevelV6)
|
||||
where
|
||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
||||
{
|
||||
fn destroy_toplevel(toplevel: zxdg_toplevel_v6::ZxdgToplevelV6) {
|
||||
let data = toplevel
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
if !data.wl_surface.as_ref().is_alive() {
|
||||
// the wl_surface is destroyed, this means the client is not
|
||||
// trying to change the role but it's a cleanup (possibly a
|
||||
// disconnecting client), ignore the protocol check.
|
||||
} else {
|
||||
data.shell_data
|
||||
.compositor_token
|
||||
.with_role_data(&data.wl_surface, |role_data| {
|
||||
*role_data = XdgToplevelSurfaceRole::None;
|
||||
})
|
||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
||||
if let Some(data) = data.xdg_surface.as_ref().user_data().get::<XdgSurfaceUserData>() {
|
||||
data.has_active_role.store(false, Ordering::Release);
|
||||
}
|
||||
// remove this surface from the known ones (as well as any leftover dead surface)
|
||||
data.shell_data
|
||||
|
@ -700,14 +666,11 @@ where
|
|||
* xdg_popup
|
||||
*/
|
||||
|
||||
pub(crate) fn send_popup_configure<R>(resource: &zxdg_popup_v6::ZxdgPopupV6, configure: PopupConfigure)
|
||||
where
|
||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
pub(crate) fn send_popup_configure(resource: &zxdg_popup_v6::ZxdgPopupV6, configure: PopupConfigure) {
|
||||
let data = resource
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
|
||||
let serial = configure.serial;
|
||||
|
@ -721,28 +684,20 @@ where
|
|||
data.xdg_surface.configure(serial.into());
|
||||
}
|
||||
|
||||
fn make_popup_handle<R: 'static>(resource: &zxdg_popup_v6::ZxdgPopupV6) -> super::PopupSurface<R> {
|
||||
fn make_popup_handle(resource: &zxdg_popup_v6::ZxdgPopupV6) -> super::PopupSurface {
|
||||
let data = resource
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.get::<ShellSurfaceUserData>()
|
||||
.unwrap();
|
||||
super::PopupSurface {
|
||||
wl_surface: data.wl_surface.clone(),
|
||||
shell_surface: PopupKind::ZxdgV6(resource.clone()),
|
||||
token: data.shell_data.compositor_token,
|
||||
}
|
||||
}
|
||||
|
||||
fn popup_implementation<R>(request: zxdg_popup_v6::Request, popup: zxdg_popup_v6::ZxdgPopupV6)
|
||||
where
|
||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = popup
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.unwrap();
|
||||
fn popup_implementation(request: zxdg_popup_v6::Request, popup: zxdg_popup_v6::ZxdgPopupV6) {
|
||||
let data = popup.as_ref().user_data().get::<ShellSurfaceUserData>().unwrap();
|
||||
match request {
|
||||
zxdg_popup_v6::Request::Destroy => {
|
||||
// all is handled by our destructor
|
||||
|
@ -761,26 +716,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn destroy_popup<R>(popup: zxdg_popup_v6::ZxdgPopupV6)
|
||||
where
|
||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
||||
{
|
||||
let data = popup
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<ShellSurfaceUserData<R>>()
|
||||
.unwrap();
|
||||
if !data.wl_surface.as_ref().is_alive() {
|
||||
// the wl_surface is destroyed, this means the client is not
|
||||
// trying to change the role but it's a cleanup (possibly a
|
||||
// disconnecting client), ignore the protocol check.
|
||||
} else {
|
||||
data.shell_data
|
||||
.compositor_token
|
||||
.with_role_data(&data.wl_surface, |role_data| {
|
||||
*role_data = XdgPopupSurfaceRole::None;
|
||||
})
|
||||
.expect("xdg_popup exists but surface has not shell_surface role?!");
|
||||
fn destroy_popup(popup: zxdg_popup_v6::ZxdgPopupV6) {
|
||||
let data = popup.as_ref().user_data().get::<ShellSurfaceUserData>().unwrap();
|
||||
if let Some(data) = data.xdg_surface.as_ref().user_data().get::<XdgSurfaceUserData>() {
|
||||
data.has_active_role.store(false, Ordering::Release);
|
||||
}
|
||||
// remove this surface from the known ones (as well as any leftover dead surface)
|
||||
data.shell_data
|
||||
|
|
Loading…
Reference in New Issue