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" ]
|
members = [ "anvil" ]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
appendlist = "1.4"
|
||||||
bitflags = "1"
|
bitflags = "1"
|
||||||
calloop = "0.8.0"
|
calloop = "0.8.0"
|
||||||
cgmath = "0.18.0"
|
cgmath = "0.18.0"
|
||||||
dbus = { version = "0.9.0", optional = true }
|
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-fourcc = "^2.1.1"
|
||||||
drm = { version = "0.4.0", optional = true }
|
drm = { version = "0.4.0", optional = true }
|
||||||
drm-ffi = { version = "0.1.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 }
|
image = { version = "0.23.14", default-features = false, optional = true }
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
libc = "0.2.70"
|
libc = "0.2.70"
|
||||||
|
libseat= { version = "0.1.1", optional = true }
|
||||||
libloading = "0.7.0"
|
libloading = "0.7.0"
|
||||||
nix = "0.20"
|
nix = "0.20"
|
||||||
slog = "2"
|
slog = "2"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#![allow(clippy::too_many_arguments)]
|
#![allow(clippy::too_many_arguments)]
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::{cell::RefCell, sync::Mutex};
|
||||||
|
|
||||||
use slog::Logger;
|
use slog::Logger;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
|
@ -11,13 +11,15 @@ use smithay::{
|
||||||
reexports::wayland_server::protocol::{wl_buffer, wl_surface},
|
reexports::wayland_server::protocol::{wl_buffer, wl_surface},
|
||||||
utils::Rectangle,
|
utils::Rectangle,
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::{roles::Role, Damage, SubsurfaceRole, TraversalAction},
|
compositor::{
|
||||||
data_device::DnDIconRole,
|
get_role, with_states, with_surface_tree_upward, Damage, SubsurfaceCachedState,
|
||||||
seat::CursorImageRole,
|
SurfaceAttributes, TraversalAction,
|
||||||
|
},
|
||||||
|
seat::CursorImageAttributes,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData};
|
use crate::{shell::SurfaceData, window_map::WindowMap};
|
||||||
|
|
||||||
struct BufferTextures<T> {
|
struct BufferTextures<T> {
|
||||||
buffer: Option<wl_buffer::WlBuffer>,
|
buffer: Option<wl_buffer::WlBuffer>,
|
||||||
|
@ -37,7 +39,6 @@ pub fn draw_cursor<R, E, F, T>(
|
||||||
frame: &mut F,
|
frame: &mut F,
|
||||||
surface: &wl_surface::WlSurface,
|
surface: &wl_surface::WlSurface,
|
||||||
(x, y): (i32, i32),
|
(x, y): (i32, i32),
|
||||||
token: MyCompositorToken,
|
|
||||||
log: &Logger,
|
log: &Logger,
|
||||||
) -> Result<(), SwapBuffersError>
|
) -> Result<(), SwapBuffersError>
|
||||||
where
|
where
|
||||||
|
@ -46,9 +47,21 @@ where
|
||||||
E: std::error::Error + Into<SwapBuffersError>,
|
E: std::error::Error + Into<SwapBuffersError>,
|
||||||
T: Texture + 'static,
|
T: Texture + 'static,
|
||||||
{
|
{
|
||||||
let (dx, dy) = match token.with_role_data::<CursorImageRole, _, _>(surface, |data| data.hotspot) {
|
let ret = with_states(surface, |states| {
|
||||||
Ok(h) => h,
|
Some(
|
||||||
Err(_) => {
|
states
|
||||||
|
.data_map
|
||||||
|
.get::<Mutex<CursorImageAttributes>>()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.hotspot,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or(None);
|
||||||
|
let (dx, dy) = match ret {
|
||||||
|
Some(h) => h,
|
||||||
|
None => {
|
||||||
warn!(
|
warn!(
|
||||||
log,
|
log,
|
||||||
"Trying to display as a cursor a surface that does not have the CursorImage role."
|
"Trying to display as a cursor a surface that does not have the CursorImage role."
|
||||||
|
@ -56,7 +69,7 @@ where
|
||||||
(0, 0)
|
(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>(
|
fn draw_surface_tree<R, E, F, T>(
|
||||||
|
@ -64,7 +77,6 @@ fn draw_surface_tree<R, E, F, T>(
|
||||||
frame: &mut F,
|
frame: &mut F,
|
||||||
root: &wl_surface::WlSurface,
|
root: &wl_surface::WlSurface,
|
||||||
location: (i32, i32),
|
location: (i32, i32),
|
||||||
compositor_token: MyCompositorToken,
|
|
||||||
log: &Logger,
|
log: &Logger,
|
||||||
) -> Result<(), SwapBuffersError>
|
) -> Result<(), SwapBuffersError>
|
||||||
where
|
where
|
||||||
|
@ -75,15 +87,16 @@ where
|
||||||
{
|
{
|
||||||
let mut result = Ok(());
|
let mut result = Ok(());
|
||||||
|
|
||||||
compositor_token.with_surface_tree_upward(
|
with_surface_tree_upward(
|
||||||
root,
|
root,
|
||||||
location,
|
location,
|
||||||
|_surface, attributes, role, &(mut x, mut y)| {
|
|_surface, states, &(mut x, mut y)| {
|
||||||
// Pull a new buffer if available
|
// 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 mut data = data.borrow_mut();
|
||||||
|
let attributes = states.cached_state.current::<SurfaceAttributes>();
|
||||||
if data.texture.is_none() {
|
if data.texture.is_none() {
|
||||||
if let Some(buffer) = data.current_state.buffer.take() {
|
if let Some(buffer) = data.buffer.take() {
|
||||||
let damage = attributes
|
let damage = attributes
|
||||||
.damage
|
.damage
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -94,16 +107,18 @@ where
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
match renderer.import_buffer(&buffer, Some(&attributes), &damage) {
|
match renderer.import_buffer(&buffer, Some(states), &damage) {
|
||||||
Some(Ok(m)) => {
|
Some(Ok(m)) => {
|
||||||
if let Some(BufferType::Shm) = buffer_type(&buffer) {
|
let texture_buffer = if let Some(BufferType::Shm) = buffer_type(&buffer) {
|
||||||
buffer.release();
|
buffer.release();
|
||||||
}
|
None
|
||||||
|
} else {
|
||||||
|
Some(buffer)
|
||||||
|
};
|
||||||
data.texture = Some(Box::new(BufferTextures {
|
data.texture = Some(Box::new(BufferTextures {
|
||||||
buffer: Some(buffer),
|
buffer: texture_buffer,
|
||||||
texture: m,
|
texture: m,
|
||||||
})
|
}))
|
||||||
as Box<dyn std::any::Any + 'static>)
|
|
||||||
}
|
}
|
||||||
Some(Err(err)) => {
|
Some(Err(err)) => {
|
||||||
warn!(log, "Error loading buffer: {:?}", err);
|
warn!(log, "Error loading buffer: {:?}", err);
|
||||||
|
@ -119,9 +134,10 @@ where
|
||||||
// Now, should we be drawn ?
|
// Now, should we be drawn ?
|
||||||
if data.texture.is_some() {
|
if data.texture.is_some() {
|
||||||
// if yes, also process the children
|
// if yes, also process the children
|
||||||
if Role::<SubsurfaceRole>::has(role) {
|
if states.role == Some("subsurface") {
|
||||||
x += data.current_state.sub_location.0;
|
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
||||||
y += data.current_state.sub_location.1;
|
x += current.location.0;
|
||||||
|
y += current.location.1;
|
||||||
}
|
}
|
||||||
TraversalAction::DoChildren((x, y))
|
TraversalAction::DoChildren((x, y))
|
||||||
} else {
|
} else {
|
||||||
|
@ -133,10 +149,9 @@ where
|
||||||
TraversalAction::SkipChildren
|
TraversalAction::SkipChildren
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|_surface, attributes, role, &(mut x, mut y)| {
|
|_surface, states, &(mut x, mut y)| {
|
||||||
if let Some(ref data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
|
if let Some(ref data) = states.data_map.get::<RefCell<SurfaceData>>() {
|
||||||
let mut data = data.borrow_mut();
|
let mut data = data.borrow_mut();
|
||||||
let (sub_x, sub_y) = data.current_state.sub_location;
|
|
||||||
if let Some(texture) = data
|
if let Some(texture) = data
|
||||||
.texture
|
.texture
|
||||||
.as_mut()
|
.as_mut()
|
||||||
|
@ -144,9 +159,10 @@ where
|
||||||
{
|
{
|
||||||
// we need to re-extract the subsurface offset, as the previous closure
|
// we need to re-extract the subsurface offset, as the previous closure
|
||||||
// only passes it to our children
|
// only passes it to our children
|
||||||
if Role::<SubsurfaceRole>::has(role) {
|
if states.role == Some("subsurface") {
|
||||||
x += sub_x;
|
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
||||||
y += sub_y;
|
x += current.location.0;
|
||||||
|
y += current.location.1;
|
||||||
}
|
}
|
||||||
if let Err(err) = frame.render_texture_at(
|
if let Err(err) = frame.render_texture_at(
|
||||||
&texture.texture,
|
&texture.texture,
|
||||||
|
@ -159,7 +175,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|_, _, _, _| true,
|
|_, _, _| true,
|
||||||
);
|
);
|
||||||
|
|
||||||
result
|
result
|
||||||
|
@ -168,9 +184,8 @@ where
|
||||||
pub fn draw_windows<R, E, F, T>(
|
pub fn draw_windows<R, E, F, T>(
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
frame: &mut F,
|
frame: &mut F,
|
||||||
window_map: &MyWindowMap,
|
window_map: &WindowMap,
|
||||||
output_rect: Option<Rectangle>,
|
output_rect: Option<Rectangle>,
|
||||||
compositor_token: MyCompositorToken,
|
|
||||||
log: &::slog::Logger,
|
log: &::slog::Logger,
|
||||||
) -> Result<(), SwapBuffersError>
|
) -> Result<(), SwapBuffersError>
|
||||||
where
|
where
|
||||||
|
@ -192,9 +207,7 @@ where
|
||||||
}
|
}
|
||||||
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
||||||
// this surface is a root of a subsurface tree that needs to be drawn
|
// this surface is a root of a subsurface tree that needs to be drawn
|
||||||
if let Err(err) =
|
if let Err(err) = draw_surface_tree(renderer, frame, &wl_surface, initial_place, log) {
|
||||||
draw_surface_tree(renderer, frame, &wl_surface, initial_place, compositor_token, log)
|
|
||||||
{
|
|
||||||
result = Err(err);
|
result = Err(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,7 +221,6 @@ pub fn draw_dnd_icon<R, E, F, T>(
|
||||||
frame: &mut F,
|
frame: &mut F,
|
||||||
surface: &wl_surface::WlSurface,
|
surface: &wl_surface::WlSurface,
|
||||||
(x, y): (i32, i32),
|
(x, y): (i32, i32),
|
||||||
token: MyCompositorToken,
|
|
||||||
log: &::slog::Logger,
|
log: &::slog::Logger,
|
||||||
) -> Result<(), SwapBuffersError>
|
) -> Result<(), SwapBuffersError>
|
||||||
where
|
where
|
||||||
|
@ -217,11 +229,11 @@ where
|
||||||
E: std::error::Error + Into<SwapBuffersError>,
|
E: std::error::Error + Into<SwapBuffersError>,
|
||||||
T: Texture + 'static,
|
T: Texture + 'static,
|
||||||
{
|
{
|
||||||
if !token.has_role::<DnDIconRole>(surface) {
|
if get_role(surface) != Some("dnd_icon") {
|
||||||
warn!(
|
warn!(
|
||||||
log,
|
log,
|
||||||
"Trying to display as a dnd icon a surface that does not have the DndIcon role."
|
"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]
|
#[macro_use]
|
||||||
extern crate slog;
|
extern crate slog;
|
||||||
#[macro_use(define_roles)]
|
|
||||||
extern crate smithay;
|
|
||||||
|
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
|
|
@ -9,25 +9,22 @@ use smithay::{
|
||||||
reexports::{
|
reexports::{
|
||||||
wayland_protocols::xdg_shell::server::xdg_toplevel,
|
wayland_protocols::xdg_shell::server::xdg_toplevel,
|
||||||
wayland_server::{
|
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,
|
Display,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
utils::Rectangle,
|
utils::Rectangle,
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::{
|
compositor::{
|
||||||
compositor_init, roles::Role, BufferAssignment, CompositorToken, RegionAttributes,
|
compositor_init, is_sync_subsurface, with_states, with_surface_tree_upward, BufferAssignment,
|
||||||
SubsurfaceRole, SurfaceEvent, TraversalAction,
|
SurfaceAttributes, TraversalAction,
|
||||||
},
|
},
|
||||||
data_device::DnDIconRole,
|
seat::{AxisFrame, GrabStartData, PointerGrab, PointerInnerHandle, Seat},
|
||||||
seat::{AxisFrame, CursorImageRole, GrabStartData, PointerGrab, PointerInnerHandle, Seat},
|
|
||||||
shell::{
|
shell::{
|
||||||
legacy::{
|
legacy::{wl_shell_init, ShellRequest, ShellState as WlShellState, ShellSurfaceKind},
|
||||||
wl_shell_init, ShellRequest, ShellState as WlShellState, ShellSurfaceKind, ShellSurfaceRole,
|
|
||||||
},
|
|
||||||
xdg::{
|
xdg::{
|
||||||
xdg_shell_init, Configure, ShellState as XdgShellState, XdgPopupSurfaceRole, XdgRequest,
|
xdg_shell_init, Configure, ShellState as XdgShellState, SurfaceCachedState, XdgRequest,
|
||||||
XdgToplevelSurfaceRole,
|
XdgToplevelSurfaceRoleAttributes,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Serial,
|
Serial,
|
||||||
|
@ -39,27 +36,10 @@ use crate::{
|
||||||
window_map::{Kind as SurfaceKind, PopupKind, WindowMap},
|
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 {
|
struct MoveSurfaceGrab {
|
||||||
start_data: GrabStartData,
|
start_data: GrabStartData,
|
||||||
window_map: Rc<RefCell<MyWindowMap>>,
|
window_map: Rc<RefCell<WindowMap>>,
|
||||||
toplevel: SurfaceKind<Roles>,
|
toplevel: SurfaceKind,
|
||||||
initial_window_location: (i32, i32),
|
initial_window_location: (i32, i32),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,8 +130,7 @@ impl From<ResizeEdge> for xdg_toplevel::ResizeEdge {
|
||||||
|
|
||||||
struct ResizeSurfaceGrab {
|
struct ResizeSurfaceGrab {
|
||||||
start_data: GrabStartData,
|
start_data: GrabStartData,
|
||||||
ctoken: MyCompositorToken,
|
toplevel: SurfaceKind,
|
||||||
toplevel: SurfaceKind<Roles>,
|
|
||||||
edges: ResizeEdge,
|
edges: ResizeEdge,
|
||||||
initial_window_size: (i32, i32),
|
initial_window_size: (i32, i32),
|
||||||
last_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;
|
new_window_height = (self.initial_window_size.1 as f64 + dy) as i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (min_size, max_size) =
|
let (min_size, max_size) = with_states(self.toplevel.get_surface().unwrap(), |states| {
|
||||||
self.ctoken
|
let data = states.cached_state.current::<SurfaceCachedState>();
|
||||||
.with_surface_data(self.toplevel.get_surface().unwrap(), |attrs| {
|
|
||||||
let data = attrs.user_data.get::<RefCell<SurfaceData>>().unwrap().borrow();
|
|
||||||
(data.min_size, data.max_size)
|
(data.min_size, data.max_size)
|
||||||
});
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let min_width = min_size.0.max(1);
|
let min_width = min_size.0.max(1);
|
||||||
let min_height = min_size.1.max(1);
|
let min_height = min_size.1.max(1);
|
||||||
|
@ -224,13 +202,11 @@ impl PointerGrab for ResizeSurfaceGrab {
|
||||||
|
|
||||||
match &self.toplevel {
|
match &self.toplevel {
|
||||||
SurfaceKind::Xdg(xdg) => {
|
SurfaceKind::Xdg(xdg) => {
|
||||||
if xdg
|
let ret = xdg.with_pending_state(|state| {
|
||||||
.with_pending_state(|state| {
|
|
||||||
state.states.set(xdg_toplevel::State::Resizing);
|
state.states.set(xdg_toplevel::State::Resizing);
|
||||||
state.size = Some(self.last_window_size);
|
state.size = Some(self.last_window_size);
|
||||||
})
|
});
|
||||||
.is_ok()
|
if ret.is_ok() {
|
||||||
{
|
|
||||||
xdg.send_configure();
|
xdg.send_configure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,20 +240,17 @@ impl PointerGrab for ResizeSurfaceGrab {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let SurfaceKind::Xdg(xdg) = &self.toplevel {
|
if let SurfaceKind::Xdg(xdg) = &self.toplevel {
|
||||||
if xdg
|
let ret = xdg.with_pending_state(|state| {
|
||||||
.with_pending_state(|state| {
|
|
||||||
state.states.unset(xdg_toplevel::State::Resizing);
|
state.states.unset(xdg_toplevel::State::Resizing);
|
||||||
state.size = Some(self.last_window_size);
|
state.size = Some(self.last_window_size);
|
||||||
})
|
});
|
||||||
.is_ok()
|
if ret.is_ok() {
|
||||||
{
|
|
||||||
xdg.send_configure();
|
xdg.send_configure();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ctoken
|
with_states(self.toplevel.get_surface().unwrap(), |states| {
|
||||||
.with_surface_data(self.toplevel.get_surface().unwrap(), |attrs| {
|
let mut data = states
|
||||||
let mut data = attrs
|
.data_map
|
||||||
.user_data
|
|
||||||
.get::<RefCell<SurfaceData>>()
|
.get::<RefCell<SurfaceData>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.borrow_mut();
|
.borrow_mut();
|
||||||
|
@ -286,12 +259,12 @@ impl PointerGrab for ResizeSurfaceGrab {
|
||||||
} else {
|
} else {
|
||||||
panic!("invalid resize state: {:?}", data.resize_state);
|
panic!("invalid resize state: {:?}", data.resize_state);
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.unwrap();
|
||||||
} else {
|
} else {
|
||||||
self.ctoken
|
with_states(self.toplevel.get_surface().unwrap(), |states| {
|
||||||
.with_surface_data(self.toplevel.get_surface().unwrap(), |attrs| {
|
let mut data = states
|
||||||
let mut data = attrs
|
.data_map
|
||||||
.user_data
|
|
||||||
.get::<RefCell<SurfaceData>>()
|
.get::<RefCell<SurfaceData>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.borrow_mut();
|
.borrow_mut();
|
||||||
|
@ -300,7 +273,8 @@ impl PointerGrab for ResizeSurfaceGrab {
|
||||||
} else {
|
} else {
|
||||||
panic!("invalid resize state: {:?}", data.resize_state);
|
panic!("invalid resize state: {:?}", data.resize_state);
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -316,34 +290,30 @@ impl PointerGrab for ResizeSurfaceGrab {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ShellHandles {
|
pub struct ShellHandles {
|
||||||
pub token: CompositorToken<Roles>,
|
pub xdg_state: Arc<Mutex<XdgShellState>>,
|
||||||
pub xdg_state: Arc<Mutex<XdgShellState<Roles>>>,
|
pub wl_state: Arc<Mutex<WlShellState>>,
|
||||||
pub wl_state: Arc<Mutex<WlShellState<Roles>>>,
|
pub window_map: Rc<RefCell<WindowMap>>,
|
||||||
pub window_map: Rc<RefCell<MyWindowMap>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logger) -> ShellHandles {
|
pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logger) -> ShellHandles {
|
||||||
// Create the compositor
|
// Create the compositor
|
||||||
let (compositor_token, _, _) = compositor_init(
|
compositor_init(
|
||||||
display,
|
display,
|
||||||
move |request, surface, ctoken, mut ddata| match request {
|
move |surface, mut ddata| {
|
||||||
SurfaceEvent::Commit => {
|
|
||||||
let anvil_state = ddata.get::<AnvilState<BackendData>>().unwrap();
|
let anvil_state = ddata.get::<AnvilState<BackendData>>().unwrap();
|
||||||
let window_map = anvil_state.window_map.as_ref();
|
let window_map = anvil_state.window_map.as_ref();
|
||||||
surface_commit(&surface, ctoken, &*window_map)
|
surface_commit(&surface, &*window_map)
|
||||||
}
|
|
||||||
},
|
},
|
||||||
log.clone(),
|
log.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Init a window map, to track the location of our windows
|
// 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
|
// init the xdg_shell
|
||||||
let xdg_window_map = window_map.clone();
|
let xdg_window_map = window_map.clone();
|
||||||
let (xdg_shell_state, _, _) = xdg_shell_init(
|
let (xdg_shell_state, _, _) = xdg_shell_init(
|
||||||
display,
|
display,
|
||||||
compositor_token,
|
|
||||||
move |shell_event| match shell_event {
|
move |shell_event| match shell_event {
|
||||||
XdgRequest::NewToplevel { surface } => {
|
XdgRequest::NewToplevel { surface } => {
|
||||||
// place the window at a random location in the [0;800]x[0;800] square
|
// 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 geometry = xdg_window_map.borrow().geometry(&toplevel).unwrap();
|
||||||
let initial_window_size = (geometry.width, geometry.height);
|
let initial_window_size = (geometry.width, geometry.height);
|
||||||
|
|
||||||
compositor_token.with_surface_data(surface.get_surface().unwrap(), move |attrs| {
|
with_states(surface.get_surface().unwrap(), move |states| {
|
||||||
attrs
|
states
|
||||||
.user_data
|
.data_map
|
||||||
.get::<RefCell<SurfaceData>>()
|
.get::<RefCell<SurfaceData>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
|
@ -452,11 +422,11 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
||||||
initial_window_location,
|
initial_window_location,
|
||||||
initial_window_size,
|
initial_window_size,
|
||||||
});
|
});
|
||||||
});
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let grab = ResizeSurfaceGrab {
|
let grab = ResizeSurfaceGrab {
|
||||||
start_data,
|
start_data,
|
||||||
ctoken: compositor_token,
|
|
||||||
toplevel,
|
toplevel,
|
||||||
edges: edges.into(),
|
edges: edges.into(),
|
||||||
initial_window_size,
|
initial_window_size,
|
||||||
|
@ -466,18 +436,20 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
||||||
pointer.set_grab(grab, serial);
|
pointer.set_grab(grab, serial);
|
||||||
}
|
}
|
||||||
XdgRequest::AckConfigure {
|
XdgRequest::AckConfigure {
|
||||||
surface, configure, ..
|
surface,
|
||||||
|
configure: Configure::Toplevel(configure),
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
if let Configure::Toplevel(configure) = configure {
|
let waiting_for_serial = with_states(&surface, |states| {
|
||||||
let waiting_for_serial = compositor_token.with_surface_data(&surface, |attrs| {
|
if let Some(data) = states.data_map.get::<RefCell<SurfaceData>>() {
|
||||||
if let Some(data) = attrs.user_data.get::<RefCell<SurfaceData>>() {
|
|
||||||
if let ResizeState::WaitingForFinalAck(_, serial) = data.borrow().resize_state {
|
if let ResizeState::WaitingForFinalAck(_, serial) = data.borrow().resize_state {
|
||||||
return Some(serial);
|
return Some(serial);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
});
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
if let Some(serial) = waiting_for_serial {
|
if let Some(serial) = waiting_for_serial {
|
||||||
if configure.serial > serial {
|
if configure.serial > serial {
|
||||||
|
@ -486,73 +458,64 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
||||||
// this case anyway
|
// this case anyway
|
||||||
}
|
}
|
||||||
|
|
||||||
if serial == configure.serial {
|
if serial == configure.serial
|
||||||
if configure.state.states.contains(xdg_toplevel::State::Resizing) {
|
&& configure.state.states.contains(xdg_toplevel::State::Resizing)
|
||||||
compositor_token.with_surface_data(&surface, |attrs| {
|
{
|
||||||
let mut data = attrs
|
with_states(&surface, |states| {
|
||||||
.user_data
|
let mut data = states
|
||||||
|
.data_map
|
||||||
.get::<RefCell<SurfaceData>>()
|
.get::<RefCell<SurfaceData>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.borrow_mut();
|
.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);
|
data.resize_state = ResizeState::WaitingForCommit(resize_data);
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
.unwrap();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
XdgRequest::Fullscreen { surface, output, .. } => {
|
XdgRequest::Fullscreen { surface, output, .. } => {
|
||||||
if surface
|
let ret = surface.with_pending_state(|state| {
|
||||||
.with_pending_state(|state| {
|
|
||||||
// TODO: Use size of current output the window is on and set position to (0,0)
|
// TODO: Use size of current output the window is on and set position to (0,0)
|
||||||
state.states.set(xdg_toplevel::State::Fullscreen);
|
state.states.set(xdg_toplevel::State::Fullscreen);
|
||||||
state.size = Some((800, 600));
|
state.size = Some((800, 600));
|
||||||
// TODO: If the provided output is None, use the output where
|
// TODO: If the provided output is None, use the output where
|
||||||
// the toplevel is currently shown
|
// the toplevel is currently shown
|
||||||
state.fullscreen_output = output;
|
state.fullscreen_output = output;
|
||||||
})
|
});
|
||||||
.is_ok()
|
if ret.is_ok() {
|
||||||
{
|
|
||||||
surface.send_configure();
|
surface.send_configure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
XdgRequest::UnFullscreen { surface } => {
|
XdgRequest::UnFullscreen { surface } => {
|
||||||
if surface
|
let ret = surface.with_pending_state(|state| {
|
||||||
.with_pending_state(|state| {
|
|
||||||
state.states.unset(xdg_toplevel::State::Fullscreen);
|
state.states.unset(xdg_toplevel::State::Fullscreen);
|
||||||
state.size = None;
|
state.size = None;
|
||||||
state.fullscreen_output = None;
|
state.fullscreen_output = None;
|
||||||
})
|
});
|
||||||
.is_ok()
|
if ret.is_ok() {
|
||||||
{
|
|
||||||
surface.send_configure();
|
surface.send_configure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
XdgRequest::Maximize { surface } => {
|
XdgRequest::Maximize { surface } => {
|
||||||
if surface
|
let ret = surface.with_pending_state(|state| {
|
||||||
.with_pending_state(|state| {
|
|
||||||
// TODO: Use size of current output the window is on and set position to (0,0)
|
// TODO: Use size of current output the window is on and set position to (0,0)
|
||||||
state.states.set(xdg_toplevel::State::Maximized);
|
state.states.set(xdg_toplevel::State::Maximized);
|
||||||
state.size = Some((800, 600));
|
state.size = Some((800, 600));
|
||||||
})
|
});
|
||||||
.is_ok()
|
if ret.is_ok() {
|
||||||
{
|
|
||||||
surface.send_configure();
|
surface.send_configure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
XdgRequest::UnMaximize { surface } => {
|
XdgRequest::UnMaximize { surface } => {
|
||||||
if surface
|
let ret = surface.with_pending_state(|state| {
|
||||||
.with_pending_state(|state| {
|
|
||||||
state.states.unset(xdg_toplevel::State::Maximized);
|
state.states.unset(xdg_toplevel::State::Maximized);
|
||||||
state.size = None;
|
state.size = None;
|
||||||
})
|
});
|
||||||
.is_ok()
|
if ret.is_ok() {
|
||||||
{
|
|
||||||
surface.send_configure();
|
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 shell_window_map = window_map.clone();
|
||||||
let (wl_shell_state, _) = wl_shell_init(
|
let (wl_shell_state, _) = wl_shell_init(
|
||||||
display,
|
display,
|
||||||
compositor_token,
|
move |req: ShellRequest| {
|
||||||
move |req: ShellRequest<_>| {
|
|
||||||
match req {
|
match req {
|
||||||
ShellRequest::SetKind {
|
ShellRequest::SetKind {
|
||||||
surface,
|
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 geometry = shell_window_map.borrow().geometry(&toplevel).unwrap();
|
||||||
let initial_window_size = (geometry.width, geometry.height);
|
let initial_window_size = (geometry.width, geometry.height);
|
||||||
|
|
||||||
compositor_token.with_surface_data(surface.get_surface().unwrap(), move |attrs| {
|
with_states(surface.get_surface().unwrap(), move |states| {
|
||||||
attrs
|
states
|
||||||
.user_data
|
.data_map
|
||||||
.get::<RefCell<SurfaceData>>()
|
.get::<RefCell<SurfaceData>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
|
@ -669,11 +631,11 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
||||||
initial_window_location,
|
initial_window_location,
|
||||||
initial_window_size,
|
initial_window_size,
|
||||||
});
|
});
|
||||||
});
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let grab = ResizeSurfaceGrab {
|
let grab = ResizeSurfaceGrab {
|
||||||
start_data,
|
start_data,
|
||||||
ctoken: compositor_token,
|
|
||||||
toplevel,
|
toplevel,
|
||||||
edges: edges.into(),
|
edges: edges.into(),
|
||||||
initial_window_size,
|
initial_window_size,
|
||||||
|
@ -689,7 +651,6 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
||||||
);
|
);
|
||||||
|
|
||||||
ShellHandles {
|
ShellHandles {
|
||||||
token: compositor_token,
|
|
||||||
xdg_state: xdg_shell_state,
|
xdg_state: xdg_shell_state,
|
||||||
wl_state: wl_shell_state,
|
wl_state: wl_shell_state,
|
||||||
window_map,
|
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)]
|
#[derive(Default)]
|
||||||
pub struct SurfaceData {
|
pub struct SurfaceData {
|
||||||
|
pub buffer: Option<wl_buffer::WlBuffer>,
|
||||||
pub texture: Option<Box<dyn std::any::Any + 'static>>,
|
pub texture: Option<Box<dyn std::any::Any + 'static>>,
|
||||||
pub geometry: Option<Rectangle>,
|
pub geometry: Option<Rectangle>,
|
||||||
pub resize_state: ResizeState,
|
pub resize_state: ResizeState,
|
||||||
/// Minimum width and height, as requested by the surface.
|
pub dimensions: Option<(i32, i32)>,
|
||||||
///
|
|
||||||
/// `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>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SurfaceData {
|
impl SurfaceData {
|
||||||
/// Apply a next state into the surface current state
|
pub fn update_buffer(&mut self, attrs: &mut SurfaceAttributes) {
|
||||||
pub fn apply_state(&mut self, next_state: CommitedState) {
|
match attrs.buffer.take() {
|
||||||
if Self::merge_state(&mut self.current_state, next_state) {
|
Some(BufferAssignment::NewBuffer { buffer, .. }) => {
|
||||||
let _ = self.texture.take();
|
// 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.
|
/// Returns the size of the surface.
|
||||||
pub fn size(&self) -> Option<(i32, i32)> {
|
pub fn size(&self) -> Option<(i32, i32)> {
|
||||||
self.current_state.dimensions
|
self.dimensions
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the surface's input region contains the point.
|
/// 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() {
|
let (w, h) = match self.size() {
|
||||||
None => return false, // If the surface has no size, it can't have an input region.
|
None => return false, // If the surface has no size, it can't have an input region.
|
||||||
Some(wh) => wh,
|
Some(wh) => wh,
|
||||||
|
@ -823,170 +745,73 @@ impl SurfaceData {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there's no input region, we're done.
|
// 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;
|
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
|
/// Send the frame callback if it had been requested
|
||||||
pub fn send_frame(&mut self, time: u32) {
|
pub fn send_frame(attrs: &mut SurfaceAttributes, time: u32) {
|
||||||
for callback in self.current_state.frame_callbacks.drain(..) {
|
for callback in attrs.frame_callbacks.drain(..) {
|
||||||
callback.done(time);
|
callback.done(time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn surface_commit(
|
fn surface_commit(surface: &wl_surface::WlSurface, window_map: &RefCell<WindowMap>) {
|
||||||
surface: &wl_surface::WlSurface,
|
|
||||||
token: CompositorToken<Roles>,
|
|
||||||
window_map: &RefCell<MyWindowMap>,
|
|
||||||
) {
|
|
||||||
#[cfg(feature = "xwayland")]
|
#[cfg(feature = "xwayland")]
|
||||||
super::xwayland::commit_hook(surface);
|
super::xwayland::commit_hook(surface);
|
||||||
|
|
||||||
let mut geometry = None;
|
let mut window_map = window_map.borrow_mut();
|
||||||
let mut min_size = (0, 0);
|
|
||||||
let mut max_size = (0, 0);
|
|
||||||
|
|
||||||
if token.has_role::<XdgToplevelSurfaceRole>(surface) {
|
if !is_sync_subsurface(surface) {
|
||||||
if let Some(SurfaceKind::Xdg(xdg)) = window_map.borrow().find(surface) {
|
// Update the buffer of all child surfaces
|
||||||
xdg.commit();
|
with_surface_tree_upward(
|
||||||
}
|
surface,
|
||||||
|
(),
|
||||||
token
|
|_, _, _| TraversalAction::DoChildren(()),
|
||||||
.with_role_data(surface, |role: &mut XdgToplevelSurfaceRole| {
|
|_, states, _| {
|
||||||
if let XdgToplevelSurfaceRole::Some(attributes) = role {
|
states
|
||||||
geometry = attributes.window_geometry;
|
.data_map
|
||||||
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
|
|
||||||
.insert_if_missing(|| RefCell::new(SurfaceData::default()));
|
.insert_if_missing(|| RefCell::new(SurfaceData::default()));
|
||||||
let mut data = attributes
|
let mut data = states
|
||||||
.user_data
|
.data_map
|
||||||
.get::<RefCell<SurfaceData>>()
|
.get::<RefCell<SurfaceData>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.borrow_mut();
|
.borrow_mut();
|
||||||
|
data.update_buffer(&mut *states.cached_state.current::<SurfaceAttributes>());
|
||||||
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!();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|_, attributes, role, _| {
|
|_, _, _| true,
|
||||||
// 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>>() {
|
if let Some(toplevel) = window_map.find(surface) {
|
||||||
data.borrow_mut().apply_from_cache();
|
// send the initial configure if relevant
|
||||||
}
|
if let SurfaceKind::Xdg(ref toplevel) = toplevel {
|
||||||
}
|
let initial_configure_sent = with_states(surface, |states| {
|
||||||
}
|
states
|
||||||
},
|
.data_map
|
||||||
|_, _, _, _| true,
|
.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);
|
window_map.refresh_toplevel(&toplevel);
|
||||||
// Get the geometry outside since it uses the token, and so would block inside.
|
// Get the geometry outside since it uses the token, and so would block inside.
|
||||||
let Rectangle { width, height, .. } = window_map.geometry(&toplevel).unwrap();
|
let Rectangle { width, height, .. } = window_map.geometry(&toplevel).unwrap();
|
||||||
|
|
||||||
let new_location = token.with_surface_data(surface, |attributes| {
|
let new_location = with_states(surface, |states| {
|
||||||
let mut data = attributes
|
let mut data = states
|
||||||
.user_data
|
.data_map
|
||||||
.get::<RefCell<SurfaceData>>()
|
.get::<RefCell<SurfaceData>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.borrow_mut();
|
.borrow_mut();
|
||||||
|
@ -1027,7 +852,8 @@ fn surface_commit(
|
||||||
}
|
}
|
||||||
|
|
||||||
new_location
|
new_location
|
||||||
});
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
if let Some(location) = new_location {
|
if let Some(location) = new_location {
|
||||||
window_map.set_location(&toplevel, location);
|
window_map.set_location(&toplevel, location);
|
||||||
|
|
|
@ -13,7 +13,6 @@ use smithay::{
|
||||||
wayland_server::{protocol::wl_surface::WlSurface, Display},
|
wayland_server::{protocol::wl_surface::WlSurface, Display},
|
||||||
},
|
},
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::CompositorToken,
|
|
||||||
data_device::{default_action_chooser, init_data_device, set_data_device_focus, DataDeviceEvent},
|
data_device::{default_action_chooser, init_data_device, set_data_device_focus, DataDeviceEvent},
|
||||||
seat::{CursorImageStatus, KeyboardHandle, PointerHandle, Seat, XkbConfig},
|
seat::{CursorImageStatus, KeyboardHandle, PointerHandle, Seat, XkbConfig},
|
||||||
shm::init_shm_global,
|
shm::init_shm_global,
|
||||||
|
@ -31,8 +30,7 @@ pub struct AnvilState<BackendData> {
|
||||||
pub running: Arc<AtomicBool>,
|
pub running: Arc<AtomicBool>,
|
||||||
pub display: Rc<RefCell<Display>>,
|
pub display: Rc<RefCell<Display>>,
|
||||||
pub handle: LoopHandle<'static, AnvilState<BackendData>>,
|
pub handle: LoopHandle<'static, AnvilState<BackendData>>,
|
||||||
pub ctoken: CompositorToken<crate::shell::Roles>,
|
pub window_map: Rc<RefCell<crate::window_map::WindowMap>>,
|
||||||
pub window_map: Rc<RefCell<crate::window_map::WindowMap<crate::shell::Roles>>>,
|
|
||||||
pub dnd_icon: Arc<Mutex<Option<WlSurface>>>,
|
pub dnd_icon: Arc<Mutex<Option<WlSurface>>>,
|
||||||
pub log: slog::Logger,
|
pub log: slog::Logger,
|
||||||
// input-related fields
|
// input-related fields
|
||||||
|
@ -106,24 +104,18 @@ impl<BackendData: Backend + 'static> AnvilState<BackendData> {
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
default_action_chooser,
|
default_action_chooser,
|
||||||
shell_handles.token,
|
|
||||||
log.clone(),
|
log.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// init input
|
// init input
|
||||||
let seat_name = backend_data.seat_name();
|
let seat_name = backend_data.seat_name();
|
||||||
|
|
||||||
let (mut seat, _) = Seat::new(
|
let (mut seat, _) = Seat::new(&mut display.borrow_mut(), seat_name.clone(), log.clone());
|
||||||
&mut display.borrow_mut(),
|
|
||||||
seat_name.clone(),
|
|
||||||
shell_handles.token,
|
|
||||||
log.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let cursor_status = Arc::new(Mutex::new(CursorImageStatus::Default));
|
let cursor_status = Arc::new(Mutex::new(CursorImageStatus::Default));
|
||||||
|
|
||||||
let cursor_status2 = cursor_status.clone();
|
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
|
// TODO: hide winit system cursor when relevant
|
||||||
*cursor_status2.lock().unwrap() = new_status
|
*cursor_status2.lock().unwrap() = new_status
|
||||||
});
|
});
|
||||||
|
@ -155,7 +147,6 @@ impl<BackendData: Backend + 'static> AnvilState<BackendData> {
|
||||||
running: Arc::new(AtomicBool::new(true)),
|
running: Arc::new(AtomicBool::new(true)),
|
||||||
display,
|
display,
|
||||||
handle,
|
handle,
|
||||||
ctoken: shell_handles.token,
|
|
||||||
window_map: shell_handles.window_map,
|
window_map: shell_handles.window_map,
|
||||||
dnd_icon,
|
dnd_icon,
|
||||||
log,
|
log,
|
||||||
|
|
|
@ -51,7 +51,6 @@ use smithay::{
|
||||||
signaling::{Linkable, SignalToken, Signaler},
|
signaling::{Linkable, SignalToken, Signaler},
|
||||||
utils::Rectangle,
|
utils::Rectangle,
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::CompositorToken,
|
|
||||||
output::{Mode, Output, PhysicalProperties},
|
output::{Mode, Output, PhysicalProperties},
|
||||||
seat::CursorImageStatus,
|
seat::CursorImageStatus,
|
||||||
},
|
},
|
||||||
|
@ -66,9 +65,8 @@ use smithay::{
|
||||||
wayland::dmabuf::init_dmabuf_global,
|
wayland::dmabuf::init_dmabuf_global,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::drawing::*;
|
|
||||||
use crate::shell::{MyWindowMap, Roles};
|
|
||||||
use crate::state::{AnvilState, Backend};
|
use crate::state::{AnvilState, Backend};
|
||||||
|
use crate::{drawing::*, window_map::WindowMap};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SessionFd(RawFd);
|
pub struct SessionFd(RawFd);
|
||||||
|
@ -632,7 +630,6 @@ impl AnvilState<UdevData> {
|
||||||
crtc,
|
crtc,
|
||||||
&mut *self.window_map.borrow_mut(),
|
&mut *self.window_map.borrow_mut(),
|
||||||
&mut self.backend_data.output_map,
|
&mut self.backend_data.output_map,
|
||||||
&self.ctoken,
|
|
||||||
&self.pointer_location,
|
&self.pointer_location,
|
||||||
&device_backend.pointer_image,
|
&device_backend.pointer_image,
|
||||||
&*self.dnd_icon.lock().unwrap(),
|
&*self.dnd_icon.lock().unwrap(),
|
||||||
|
@ -678,9 +675,8 @@ fn render_surface(
|
||||||
renderer: &mut Gles2Renderer,
|
renderer: &mut Gles2Renderer,
|
||||||
device_id: dev_t,
|
device_id: dev_t,
|
||||||
crtc: crtc::Handle,
|
crtc: crtc::Handle,
|
||||||
window_map: &mut MyWindowMap,
|
window_map: &mut WindowMap,
|
||||||
output_map: &mut Vec<MyOutput>,
|
output_map: &mut Vec<MyOutput>,
|
||||||
compositor_token: &CompositorToken<Roles>,
|
|
||||||
pointer_location: &(f64, f64),
|
pointer_location: &(f64, f64),
|
||||||
pointer_image: &Gles2Texture,
|
pointer_image: &Gles2Texture,
|
||||||
dnd_icon: &Option<wl_surface::WlSurface>,
|
dnd_icon: &Option<wl_surface::WlSurface>,
|
||||||
|
@ -721,7 +717,6 @@ fn render_surface(
|
||||||
width: width as i32,
|
width: width as i32,
|
||||||
height: height as i32,
|
height: height as i32,
|
||||||
}),
|
}),
|
||||||
*compositor_token,
|
|
||||||
logger,
|
logger,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -736,14 +731,7 @@ fn render_surface(
|
||||||
{
|
{
|
||||||
if let Some(ref wl_surface) = dnd_icon.as_ref() {
|
if let Some(ref wl_surface) = dnd_icon.as_ref() {
|
||||||
if wl_surface.as_ref().is_alive() {
|
if wl_surface.as_ref().is_alive() {
|
||||||
draw_dnd_icon(
|
draw_dnd_icon(renderer, frame, wl_surface, (ptr_x, ptr_y), logger)?;
|
||||||
renderer,
|
|
||||||
frame,
|
|
||||||
wl_surface,
|
|
||||||
(ptr_x, ptr_y),
|
|
||||||
*compositor_token,
|
|
||||||
logger,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -759,20 +747,12 @@ fn render_surface(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let CursorImageStatus::Image(ref wl_surface) = *cursor_status {
|
if let CursorImageStatus::Image(ref wl_surface) = *cursor_status {
|
||||||
draw_cursor(
|
draw_cursor(renderer, frame, wl_surface, (ptr_x, ptr_y), logger)?;
|
||||||
renderer,
|
|
||||||
frame,
|
|
||||||
wl_surface,
|
|
||||||
(ptr_x, ptr_y),
|
|
||||||
*compositor_token,
|
|
||||||
logger,
|
|
||||||
)?;
|
|
||||||
} else {
|
} else {
|
||||||
frame.render_texture_at(pointer_image, (ptr_x, ptr_y), Transform::Normal, 1.0)?;
|
frame.render_texture_at(pointer_image, (ptr_x, ptr_y), Transform::Normal, 1.0)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,10 +4,10 @@ use smithay::{
|
||||||
reexports::wayland_server::protocol::wl_surface,
|
reexports::wayland_server::protocol::wl_surface,
|
||||||
utils::Rectangle,
|
utils::Rectangle,
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::{roles::Role, CompositorToken, SubsurfaceRole, TraversalAction},
|
compositor::{with_states, with_surface_tree_downward, SubsurfaceCachedState, TraversalAction},
|
||||||
shell::{
|
shell::{
|
||||||
legacy::{ShellSurface, ShellSurfaceRole},
|
legacy::ShellSurface,
|
||||||
xdg::{PopupSurface, ToplevelSurface, XdgPopupSurfaceRole, XdgToplevelSurfaceRole},
|
xdg::{PopupSurface, SurfaceCachedState, ToplevelSurface},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -16,29 +16,15 @@ use crate::shell::SurfaceData;
|
||||||
#[cfg(feature = "xwayland")]
|
#[cfg(feature = "xwayland")]
|
||||||
use crate::xwayland::X11Surface;
|
use crate::xwayland::X11Surface;
|
||||||
|
|
||||||
pub enum Kind<R> {
|
#[derive(Clone)]
|
||||||
Xdg(ToplevelSurface<R>),
|
pub enum Kind {
|
||||||
Wl(ShellSurface<R>),
|
Xdg(ToplevelSurface),
|
||||||
|
Wl(ShellSurface),
|
||||||
#[cfg(feature = "xwayland")]
|
#[cfg(feature = "xwayland")]
|
||||||
X11(X11Surface),
|
X11(X11Surface),
|
||||||
}
|
}
|
||||||
|
|
||||||
// We implement Clone manually because #[derive(..)] would require R: Clone.
|
impl Kind {
|
||||||
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,
|
|
||||||
{
|
|
||||||
pub fn alive(&self) -> bool {
|
pub fn alive(&self) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
Kind::Xdg(ref t) => t.alive(),
|
Kind::Xdg(ref t) => t.alive(),
|
||||||
|
@ -68,23 +54,12 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum PopupKind<R> {
|
#[derive(Clone)]
|
||||||
Xdg(PopupSurface<R>),
|
pub enum PopupKind {
|
||||||
|
Xdg(PopupSurface),
|
||||||
}
|
}
|
||||||
|
|
||||||
// We implement Clone manually because #[derive(..)] would require R: Clone.
|
impl PopupKind {
|
||||||
impl<R> Clone for PopupKind<R> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
match self {
|
|
||||||
PopupKind::Xdg(xdg) => PopupKind::Xdg(xdg.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R> PopupKind<R>
|
|
||||||
where
|
|
||||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
pub fn alive(&self) -> bool {
|
pub fn alive(&self) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
PopupKind::Xdg(ref t) => t.alive(),
|
PopupKind::Xdg(ref t) => t.alive(),
|
||||||
|
@ -97,56 +72,53 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Window<R> {
|
struct Window {
|
||||||
location: (i32, i32),
|
location: (i32, i32),
|
||||||
/// A bounding box over this window and its children.
|
/// 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
|
/// 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.
|
/// geometry if that's not set explicitly.
|
||||||
bbox: Rectangle,
|
bbox: Rectangle,
|
||||||
toplevel: Kind<R>,
|
toplevel: Kind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> Window<R>
|
impl Window {
|
||||||
where
|
|
||||||
R: Role<SubsurfaceRole> + Role<XdgToplevelSurfaceRole> + Role<ShellSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
/// Finds the topmost surface under this point if any and returns it together with the location of this
|
/// Finds the topmost surface under this point if any and returns it together with the location of this
|
||||||
/// surface.
|
/// surface.
|
||||||
fn matching(
|
fn matching(&self, point: (f64, f64)) -> Option<(wl_surface::WlSurface, (f64, f64))> {
|
||||||
&self,
|
|
||||||
point: (f64, f64),
|
|
||||||
ctoken: CompositorToken<R>,
|
|
||||||
) -> Option<(wl_surface::WlSurface, (f64, f64))> {
|
|
||||||
if !self.bbox.contains((point.0 as i32, point.1 as i32)) {
|
if !self.bbox.contains((point.0 as i32, point.1 as i32)) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
// need to check more carefully
|
// need to check more carefully
|
||||||
let found = RefCell::new(None);
|
let found = RefCell::new(None);
|
||||||
if let Some(wl_surface) = self.toplevel.get_surface() {
|
if let Some(wl_surface) = self.toplevel.get_surface() {
|
||||||
ctoken.with_surface_tree_downward(
|
with_surface_tree_downward(
|
||||||
wl_surface,
|
wl_surface,
|
||||||
self.location,
|
self.location,
|
||||||
|wl_surface, attributes, role, &(mut x, mut y)| {
|
|wl_surface, states, &(mut x, mut y)| {
|
||||||
let data = attributes.user_data.get::<RefCell<SurfaceData>>();
|
let data = states.data_map.get::<RefCell<SurfaceData>>();
|
||||||
|
|
||||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
if states.role == Some("subsurface") {
|
||||||
x += subdata.location.0;
|
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
||||||
y += subdata.location.1;
|
x += current.location.0;
|
||||||
|
y += current.location.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let surface_local_point = (point.0 - x as f64, point.1 - y as f64);
|
let surface_local_point = (point.0 - x as f64, point.1 - y as f64);
|
||||||
if data
|
let contains_the_point = data
|
||||||
.map(|data| data.borrow().contains_point(surface_local_point))
|
.map(|data| {
|
||||||
.unwrap_or(false)
|
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)));
|
*found.borrow_mut() = Some((wl_surface.clone(), (x as f64, y as f64)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TraversalAction::DoChildren((x, y))
|
TraversalAction::DoChildren((x, y))
|
||||||
},
|
},
|
||||||
|_, _, _, _| {},
|
|_, _, _| {},
|
||||||
|_, _, _, _| {
|
|_, _, _| {
|
||||||
// only continue if the point is not found
|
// only continue if the point is not found
|
||||||
found.borrow().is_none()
|
found.borrow().is_none()
|
||||||
},
|
},
|
||||||
|
@ -155,20 +127,21 @@ where
|
||||||
found.into_inner()
|
found.into_inner()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn self_update(&mut self, ctoken: CompositorToken<R>) {
|
fn self_update(&mut self) {
|
||||||
let (base_x, base_y) = self.location;
|
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);
|
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() {
|
if let Some(wl_surface) = self.toplevel.get_surface() {
|
||||||
ctoken.with_surface_tree_downward(
|
with_surface_tree_downward(
|
||||||
wl_surface,
|
wl_surface,
|
||||||
(base_x, base_y),
|
(base_x, base_y),
|
||||||
|_, attributes, role, &(mut x, mut y)| {
|
|_, states, &(mut x, mut y)| {
|
||||||
let data = attributes.user_data.get::<RefCell<SurfaceData>>();
|
let data = states.data_map.get::<RefCell<SurfaceData>>();
|
||||||
|
|
||||||
if let Some((w, h)) = data.and_then(|d| d.borrow().size()) {
|
if let Some((w, h)) = data.and_then(|d| d.borrow().size()) {
|
||||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
if states.role == Some("subsurface") {
|
||||||
x += subdata.location.0;
|
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
||||||
y += subdata.location.1;
|
x += current.location.0;
|
||||||
|
y += current.location.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the bounding box.
|
// Update the bounding box.
|
||||||
|
@ -184,8 +157,8 @@ where
|
||||||
TraversalAction::SkipChildren
|
TraversalAction::SkipChildren
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|_, _, _, _| {},
|
|_, _, _| {},
|
||||||
|_, _, _, _| true,
|
|_, _, _| true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
self.bbox = Rectangle {
|
self.bbox = Rectangle {
|
||||||
|
@ -197,85 +170,69 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the geometry of this window.
|
/// 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.
|
// It's the set geometry with the full bounding box as the fallback.
|
||||||
ctoken
|
with_states(self.toplevel.get_surface().unwrap(), |states| {
|
||||||
.with_surface_data(self.toplevel.get_surface().unwrap(), |attributes| {
|
states.cached_state.current::<SurfaceCachedState>().geometry
|
||||||
attributes
|
|
||||||
.user_data
|
|
||||||
.get::<RefCell<SurfaceData>>()
|
|
||||||
.unwrap()
|
|
||||||
.borrow()
|
|
||||||
.geometry
|
|
||||||
})
|
})
|
||||||
|
.unwrap()
|
||||||
.unwrap_or(self.bbox)
|
.unwrap_or(self.bbox)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends the frame callback to all the subsurfaces in this
|
/// Sends the frame callback to all the subsurfaces in this
|
||||||
/// window that requested it
|
/// 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() {
|
if let Some(wl_surface) = self.toplevel.get_surface() {
|
||||||
ctoken.with_surface_tree_downward(
|
with_surface_tree_downward(
|
||||||
wl_surface,
|
wl_surface,
|
||||||
(),
|
(),
|
||||||
|_, _, _, &()| TraversalAction::DoChildren(()),
|
|_, _, &()| TraversalAction::DoChildren(()),
|
||||||
|_, attributes, _, &()| {
|
|_, states, &()| {
|
||||||
// the surface may not have any user_data if it is a subsurface and has not
|
// the surface may not have any user_data if it is a subsurface and has not
|
||||||
// yet been commited
|
// yet been commited
|
||||||
if let Some(data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
|
SurfaceData::send_frame(&mut *states.cached_state.current(), time)
|
||||||
data.borrow_mut().send_frame(time)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|_, _, _, &()| true,
|
|_, _, &()| true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Popup<R> {
|
pub struct Popup {
|
||||||
popup: PopupKind<R>,
|
popup: PopupKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WindowMap<R> {
|
pub struct WindowMap {
|
||||||
ctoken: CompositorToken<R>,
|
windows: Vec<Window>,
|
||||||
windows: Vec<Window<R>>,
|
popups: Vec<Popup>,
|
||||||
popups: Vec<Popup<R>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> WindowMap<R>
|
impl WindowMap {
|
||||||
where
|
pub fn new() -> Self {
|
||||||
R: Role<SubsurfaceRole>
|
|
||||||
+ Role<XdgToplevelSurfaceRole>
|
|
||||||
+ Role<XdgPopupSurfaceRole>
|
|
||||||
+ Role<ShellSurfaceRole>
|
|
||||||
+ 'static,
|
|
||||||
{
|
|
||||||
pub fn new(ctoken: CompositorToken<R>) -> Self {
|
|
||||||
WindowMap {
|
WindowMap {
|
||||||
ctoken,
|
|
||||||
windows: Vec::new(),
|
windows: Vec::new(),
|
||||||
popups: 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 {
|
let mut window = Window {
|
||||||
location,
|
location,
|
||||||
bbox: Rectangle::default(),
|
bbox: Rectangle::default(),
|
||||||
toplevel,
|
toplevel,
|
||||||
};
|
};
|
||||||
window.self_update(self.ctoken);
|
window.self_update();
|
||||||
self.windows.insert(0, window);
|
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 };
|
let popup = Popup { popup };
|
||||||
self.popups.push(popup);
|
self.popups.push(popup);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_surface_under(&self, point: (f64, f64)) -> Option<(wl_surface::WlSurface, (f64, f64))> {
|
pub fn get_surface_under(&self, point: (f64, f64)) -> Option<(wl_surface::WlSurface, (f64, f64))> {
|
||||||
for w in &self.windows {
|
for w in &self.windows {
|
||||||
if let Some(surface) = w.matching(point, self.ctoken) {
|
if let Some(surface) = w.matching(point) {
|
||||||
return Some(surface);
|
return Some(surface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -288,7 +245,7 @@ where
|
||||||
) -> Option<(wl_surface::WlSurface, (f64, f64))> {
|
) -> Option<(wl_surface::WlSurface, (f64, f64))> {
|
||||||
let mut found = None;
|
let mut found = None;
|
||||||
for (i, w) in self.windows.iter().enumerate() {
|
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));
|
found = Some((i, surface));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -304,7 +261,7 @@ where
|
||||||
|
|
||||||
pub fn with_windows_from_bottom_to_top<Func>(&self, mut f: Func)
|
pub fn with_windows_from_bottom_to_top<Func>(&self, mut f: Func)
|
||||||
where
|
where
|
||||||
Func: FnMut(&Kind<R>, (i32, i32), &Rectangle),
|
Func: FnMut(&Kind, (i32, i32), &Rectangle),
|
||||||
{
|
{
|
||||||
for w in self.windows.iter().rev() {
|
for w in self.windows.iter().rev() {
|
||||||
f(&w.toplevel, w.location, &w.bbox)
|
f(&w.toplevel, w.location, &w.bbox)
|
||||||
|
@ -315,14 +272,14 @@ where
|
||||||
self.windows.retain(|w| w.toplevel.alive());
|
self.windows.retain(|w| w.toplevel.alive());
|
||||||
self.popups.retain(|p| p.popup.alive());
|
self.popups.retain(|p| p.popup.alive());
|
||||||
for w in &mut self.windows {
|
for w in &mut self.windows {
|
||||||
w.self_update(self.ctoken);
|
w.self_update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Refreshes the state of the toplevel, if it exists.
|
/// Refreshes the state of the toplevel, if it exists.
|
||||||
pub fn refresh_toplevel(&mut self, toplevel: &Kind<R>) {
|
pub fn refresh_toplevel(&mut self, toplevel: &Kind) {
|
||||||
if let Some(w) = self.windows.iter_mut().find(|w| w.toplevel.equals(toplevel)) {
|
if let Some(w) = self.windows.iter_mut().find(|w| w.toplevel.equals(toplevel)) {
|
||||||
w.self_update(self.ctoken);
|
w.self_update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,7 +288,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds the toplevel corresponding to the given `WlSurface`.
|
/// 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| {
|
self.windows.iter().find_map(|w| {
|
||||||
if w.toplevel
|
if w.toplevel
|
||||||
.get_surface()
|
.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| {
|
self.popups.iter().find_map(|p| {
|
||||||
if p.popup
|
if p.popup
|
||||||
.get_surface()
|
.get_surface()
|
||||||
|
@ -360,7 +317,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the location of the toplevel, if it exists.
|
/// Returns the location of the toplevel, if it exists.
|
||||||
pub fn location(&self, toplevel: &Kind<R>) -> Option<(i32, i32)> {
|
pub fn location(&self, toplevel: &Kind) -> Option<(i32, i32)> {
|
||||||
self.windows
|
self.windows
|
||||||
.iter()
|
.iter()
|
||||||
.find(|w| w.toplevel.equals(toplevel))
|
.find(|w| w.toplevel.equals(toplevel))
|
||||||
|
@ -368,24 +325,24 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the location of the toplevel, if it exists.
|
/// Sets the location of the toplevel, if it exists.
|
||||||
pub fn set_location(&mut self, toplevel: &Kind<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)) {
|
if let Some(w) = self.windows.iter_mut().find(|w| w.toplevel.equals(toplevel)) {
|
||||||
w.location = location;
|
w.location = location;
|
||||||
w.self_update(self.ctoken);
|
w.self_update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the geometry of the toplevel, if it exists.
|
/// 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
|
self.windows
|
||||||
.iter()
|
.iter()
|
||||||
.find(|w| w.toplevel.equals(toplevel))
|
.find(|w| w.toplevel.equals(toplevel))
|
||||||
.map(|w| w.geometry(self.ctoken))
|
.map(|w| w.geometry())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_frames(&self, time: u32) {
|
pub fn send_frames(&self, time: u32) {
|
||||||
for window in &self.windows {
|
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
|
* Initialize the globals
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let mut state = AnvilState::init(
|
let mut state = AnvilState::init(display.clone(), event_loop.handle(), WinitData, log.clone());
|
||||||
display.clone(),
|
|
||||||
event_loop.handle(),
|
|
||||||
WinitData,
|
|
||||||
log.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let (output, _) = Output::new(
|
let (output, _) = Output::new(
|
||||||
&mut display.borrow_mut(),
|
&mut display.borrow_mut(),
|
||||||
|
@ -122,14 +117,7 @@ pub fn run_winit(
|
||||||
frame.clear([0.8, 0.8, 0.9, 1.0])?;
|
frame.clear([0.8, 0.8, 0.9, 1.0])?;
|
||||||
|
|
||||||
// draw the windows
|
// draw the windows
|
||||||
draw_windows(
|
draw_windows(renderer, frame, &*state.window_map.borrow(), None, &log)?;
|
||||||
renderer,
|
|
||||||
frame,
|
|
||||||
&*state.window_map.borrow(),
|
|
||||||
None,
|
|
||||||
state.ctoken,
|
|
||||||
&log,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let (x, y) = state.pointer_location;
|
let (x, y) = state.pointer_location;
|
||||||
// draw the dnd icon if any
|
// draw the dnd icon if any
|
||||||
|
@ -137,14 +125,7 @@ pub fn run_winit(
|
||||||
let guard = state.dnd_icon.lock().unwrap();
|
let guard = state.dnd_icon.lock().unwrap();
|
||||||
if let Some(ref surface) = *guard {
|
if let Some(ref surface) = *guard {
|
||||||
if surface.as_ref().is_alive() {
|
if surface.as_ref().is_alive() {
|
||||||
draw_dnd_icon(
|
draw_dnd_icon(renderer, frame, surface, (x as i32, y as i32), &log)?;
|
||||||
renderer,
|
|
||||||
frame,
|
|
||||||
surface,
|
|
||||||
(x as i32, y as i32),
|
|
||||||
state.ctoken,
|
|
||||||
&log,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,7 +144,7 @@ pub fn run_winit(
|
||||||
// draw as relevant
|
// draw as relevant
|
||||||
if let CursorImageStatus::Image(ref surface) = *guard {
|
if let CursorImageStatus::Image(ref surface) = *guard {
|
||||||
cursor_visible = false;
|
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 {
|
} else {
|
||||||
cursor_visible = true;
|
cursor_visible = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::{cell::RefCell, collections::HashMap, convert::TryFrom, os::unix::net::
|
||||||
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
reexports::wayland_server::{protocol::wl_surface::WlSurface, Client},
|
reexports::wayland_server::{protocol::wl_surface::WlSurface, Client},
|
||||||
wayland::compositor::CompositorToken,
|
wayland::compositor::give_role,
|
||||||
};
|
};
|
||||||
|
|
||||||
use x11rb::{
|
use x11rb::{
|
||||||
|
@ -20,8 +20,7 @@ use x11rb::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
shell::{MyWindowMap, Roles},
|
window_map::{Kind, WindowMap},
|
||||||
window_map::Kind,
|
|
||||||
AnvilState,
|
AnvilState,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,8 +36,7 @@ impl<BackendData: 'static> AnvilState<BackendData> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xwayland_ready(&mut self, connection: UnixStream, client: Client) {
|
pub fn xwayland_ready(&mut self, connection: UnixStream, client: Client) {
|
||||||
let (wm, source) =
|
let (wm, source) = X11State::start_wm(connection, self.window_map.clone(), self.log.clone()).unwrap();
|
||||||
X11State::start_wm(connection, self.ctoken, self.window_map.clone(), self.log.clone()).unwrap();
|
|
||||||
let wm = Rc::new(RefCell::new(wm));
|
let wm = Rc::new(RefCell::new(wm));
|
||||||
client.data_map().insert_if_missing(|| Rc::clone(&wm));
|
client.data_map().insert_if_missing(|| Rc::clone(&wm));
|
||||||
self.handle
|
self.handle
|
||||||
|
@ -70,15 +68,13 @@ struct X11State {
|
||||||
atoms: Atoms,
|
atoms: Atoms,
|
||||||
log: slog::Logger,
|
log: slog::Logger,
|
||||||
unpaired_surfaces: HashMap<u32, (Window, (i32, i32))>,
|
unpaired_surfaces: HashMap<u32, (Window, (i32, i32))>,
|
||||||
token: CompositorToken<Roles>,
|
window_map: Rc<RefCell<WindowMap>>,
|
||||||
window_map: Rc<RefCell<MyWindowMap>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl X11State {
|
impl X11State {
|
||||||
fn start_wm(
|
fn start_wm(
|
||||||
connection: UnixStream,
|
connection: UnixStream,
|
||||||
token: CompositorToken<Roles>,
|
window_map: Rc<RefCell<WindowMap>>,
|
||||||
window_map: Rc<RefCell<MyWindowMap>>,
|
|
||||||
log: slog::Logger,
|
log: slog::Logger,
|
||||||
) -> Result<(Self, X11Source), Box<dyn std::error::Error>> {
|
) -> Result<(Self, X11Source), Box<dyn std::error::Error>> {
|
||||||
// Create an X11 connection. XWayland only uses screen 0.
|
// Create an X11 connection. XWayland only uses screen 0.
|
||||||
|
@ -123,7 +119,6 @@ impl X11State {
|
||||||
conn: Rc::clone(&conn),
|
conn: Rc::clone(&conn),
|
||||||
atoms,
|
atoms,
|
||||||
unpaired_surfaces: Default::default(),
|
unpaired_surfaces: Default::default(),
|
||||||
token,
|
|
||||||
window_map,
|
window_map,
|
||||||
log,
|
log,
|
||||||
};
|
};
|
||||||
|
@ -209,7 +204,7 @@ impl X11State {
|
||||||
fn new_window(&mut self, window: Window, surface: WlSurface, location: (i32, i32)) {
|
fn new_window(&mut self, window: Window, surface: WlSurface, location: (i32, i32)) {
|
||||||
debug!(self.log, "Matched X11 surface {:x?} to {:x?}", window, surface);
|
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
|
// 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);
|
error!(self.log, "Surface {:x?} already has a role?!", surface);
|
||||||
return;
|
return;
|
||||||
|
@ -237,8 +232,6 @@ pub fn commit_hook(surface: &WlSurface) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct X11SurfaceRole;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct X11Surface {
|
pub struct X11Surface {
|
||||||
surface: WlSurface,
|
surface: WlSurface,
|
||||||
|
|
|
@ -39,8 +39,7 @@ impl<A: AsRawFd + 'static> Allocator<DumbBuffer<A>> for DrmDevice<A> {
|
||||||
// dumb buffers are always linear
|
// dumb buffers are always linear
|
||||||
if modifiers
|
if modifiers
|
||||||
.iter()
|
.iter()
|
||||||
.find(|x| **x == Modifier::Invalid || **x == Modifier::Linear)
|
.all(|&x| x != Modifier::Invalid && x != Modifier::Linear)
|
||||||
.is_none()
|
|
||||||
{
|
{
|
||||||
return Err(drm::SystemError::InvalidArgument);
|
return Err(drm::SystemError::InvalidArgument);
|
||||||
}
|
}
|
||||||
|
|
|
@ -372,10 +372,9 @@ impl<A: AsRawFd + 'static> LegacyDrmSurface<A> {
|
||||||
let encoders = info
|
let encoders = info
|
||||||
.encoders()
|
.encoders()
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|enc| enc.is_some())
|
.flatten()
|
||||||
.map(|enc| enc.unwrap())
|
|
||||||
.map(|encoder| {
|
.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",
|
errmsg: "Error loading encoder info",
|
||||||
dev: self.fd.dev_path(),
|
dev: self.fd.dev_path(),
|
||||||
source,
|
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 {
|
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<'_>>;
|
fn supported_platforms(&self) -> Vec<EGLPlatform<'_>>;
|
||||||
|
|
||||||
/// Type of surfaces created
|
/// Type of surfaces created
|
||||||
|
|
|
@ -35,7 +35,7 @@ use super::{ImportDma, ImportShm};
|
||||||
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
|
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
|
||||||
use crate::backend::egl::{display::EGLBufferReader, Format as EGLFormat};
|
use crate::backend::egl::{display::EGLBufferReader, Format as EGLFormat};
|
||||||
#[cfg(feature = "wayland_frontend")]
|
#[cfg(feature = "wayland_frontend")]
|
||||||
use crate::{utils::Rectangle, wayland::compositor::SurfaceAttributes};
|
use crate::utils::Rectangle;
|
||||||
#[cfg(feature = "wayland_frontend")]
|
#[cfg(feature = "wayland_frontend")]
|
||||||
use wayland_server::protocol::{wl_buffer, wl_shm};
|
use wayland_server::protocol::{wl_buffer, wl_shm};
|
||||||
|
|
||||||
|
@ -501,7 +501,7 @@ impl ImportShm for Gles2Renderer {
|
||||||
fn import_shm_buffer(
|
fn import_shm_buffer(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: &wl_buffer::WlBuffer,
|
buffer: &wl_buffer::WlBuffer,
|
||||||
surface: Option<&SurfaceAttributes>,
|
surface: Option<&crate::wayland::compositor::SurfaceData>,
|
||||||
damage: &[Rectangle],
|
damage: &[Rectangle],
|
||||||
) -> Result<Gles2Texture, Gles2Error> {
|
) -> Result<Gles2Texture, Gles2Error> {
|
||||||
use crate::wayland::shm::with_buffer_contents;
|
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.
|
// why not store a `Gles2Texture`? because the user might do so.
|
||||||
// this is guaranteed a non-public internal type, so we are good.
|
// this is guaranteed a non-public internal type, so we are good.
|
||||||
surface
|
surface
|
||||||
.and_then(|surface| surface.user_data.get::<Rc<Gles2TextureInternal>>().cloned())
|
.and_then(|surface| surface.data_map.get::<Rc<Gles2TextureInternal>>().cloned())
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
let mut tex = 0;
|
let mut tex = 0;
|
||||||
unsafe { self.gl.GenTextures(1, &mut tex) };
|
unsafe { self.gl.GenTextures(1, &mut tex) };
|
||||||
|
@ -742,9 +742,7 @@ impl Gles2Renderer {
|
||||||
texture.0.texture,
|
texture.0.texture,
|
||||||
buffer
|
buffer
|
||||||
);
|
);
|
||||||
if texture.0.is_external {
|
if !texture.0.is_external {
|
||||||
Ok(Some(texture))
|
|
||||||
} else {
|
|
||||||
if let Some(egl_images) = texture.0.egl_images.as_ref() {
|
if let Some(egl_images) = texture.0.egl_images.as_ref() {
|
||||||
if egl_images[0] == ffi_egl::NO_IMAGE_KHR {
|
if egl_images[0] == ffi_egl::NO_IMAGE_KHR {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
@ -753,8 +751,8 @@ impl Gles2Renderer {
|
||||||
let tex = Some(texture.0.texture);
|
let tex = Some(texture.0.texture);
|
||||||
self.import_egl_image(egl_images[0], false, tex)?;
|
self.import_egl_image(egl_images[0], false, tex)?;
|
||||||
}
|
}
|
||||||
Ok(Some(texture))
|
|
||||||
}
|
}
|
||||||
|
Ok(Some(texture))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use std::collections::HashSet;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
#[cfg(feature = "wayland_frontend")]
|
#[cfg(feature = "wayland_frontend")]
|
||||||
use crate::{utils::Rectangle, wayland::compositor::SurfaceAttributes};
|
use crate::{utils::Rectangle, wayland::compositor::SurfaceData};
|
||||||
use cgmath::{prelude::*, Matrix3, Vector2};
|
use cgmath::{prelude::*, Matrix3, Vector2};
|
||||||
#[cfg(feature = "wayland_frontend")]
|
#[cfg(feature = "wayland_frontend")]
|
||||||
use wayland_server::protocol::{wl_buffer, wl_shm};
|
use wayland_server::protocol::{wl_buffer, wl_shm};
|
||||||
|
@ -264,7 +264,7 @@ pub trait ImportShm: Renderer {
|
||||||
fn import_shm_buffer(
|
fn import_shm_buffer(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: &wl_buffer::WlBuffer,
|
buffer: &wl_buffer::WlBuffer,
|
||||||
surface: Option<&SurfaceAttributes>,
|
surface: Option<&crate::wayland::compositor::SurfaceData>,
|
||||||
damage: &[Rectangle],
|
damage: &[Rectangle],
|
||||||
) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>;
|
) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>;
|
||||||
|
|
||||||
|
@ -404,7 +404,7 @@ pub trait ImportAll: Renderer {
|
||||||
fn import_buffer(
|
fn import_buffer(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: &wl_buffer::WlBuffer,
|
buffer: &wl_buffer::WlBuffer,
|
||||||
surface: Option<&SurfaceAttributes>,
|
surface: Option<&crate::wayland::compositor::SurfaceData>,
|
||||||
damage: &[Rectangle],
|
damage: &[Rectangle],
|
||||||
) -> Option<Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>>;
|
) -> 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(
|
fn import_buffer(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: &wl_buffer::WlBuffer,
|
buffer: &wl_buffer::WlBuffer,
|
||||||
surface: Option<&SurfaceAttributes>,
|
surface: Option<&SurfaceData>,
|
||||||
damage: &[Rectangle],
|
damage: &[Rectangle],
|
||||||
) -> Option<Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>> {
|
) -> Option<Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>> {
|
||||||
match buffer_type(buffer) {
|
match buffer_type(buffer) {
|
||||||
|
@ -439,7 +439,7 @@ impl<R: Renderer + ImportShm + ImportDma> ImportAll for R {
|
||||||
fn import_buffer(
|
fn import_buffer(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: &wl_buffer::WlBuffer,
|
buffer: &wl_buffer::WlBuffer,
|
||||||
surface: Option<&SurfaceAttributes>,
|
surface: Option<&SurfaceData>,
|
||||||
damage: &[Rectangle],
|
damage: &[Rectangle],
|
||||||
) -> Option<Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>> {
|
) -> Option<Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>> {
|
||||||
match buffer_type(buffer) {
|
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::{
|
use wayland_server::{
|
||||||
protocol::{wl_compositor, wl_region, wl_subcompositor, wl_subsurface, wl_surface},
|
protocol::{wl_compositor, wl_region, wl_subcompositor, wl_subsurface, wl_surface},
|
||||||
|
@ -6,23 +14,22 @@ use wayland_server::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
tree::{Location, SurfaceData},
|
cache::Cacheable,
|
||||||
AlreadyHasRole, BufferAssignment, CompositorToken, Damage, Rectangle, RectangleKind, RegionAttributes,
|
tree::{Location, PrivateSurfaceData},
|
||||||
Role, RoleType, SubsurfaceRole, SurfaceEvent,
|
AlreadyHasRole, BufferAssignment, Damage, Rectangle, RectangleKind, RegionAttributes, SurfaceAttributes,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* wl_compositor
|
* wl_compositor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub(crate) fn implement_compositor<R, Impl>(
|
pub(crate) fn implement_compositor<Impl>(
|
||||||
compositor: Main<wl_compositor::WlCompositor>,
|
compositor: Main<wl_compositor::WlCompositor>,
|
||||||
log: ::slog::Logger,
|
log: ::slog::Logger,
|
||||||
implem: Rc<RefCell<Impl>>,
|
implem: Rc<RefCell<Impl>>,
|
||||||
) -> wl_compositor::WlCompositor
|
) -> wl_compositor::WlCompositor
|
||||||
where
|
where
|
||||||
R: Default + Send + 'static,
|
Impl: for<'a> FnMut(wl_surface::WlSurface, DispatchData<'a>) + 'static,
|
||||||
Impl: for<'a> FnMut(SurfaceEvent, wl_surface::WlSurface, CompositorToken<R>, DispatchData<'a>) + 'static,
|
|
||||||
{
|
{
|
||||||
compositor.quick_assign(move |_compositor, request, _| match request {
|
compositor.quick_assign(move |_compositor, request, _| match request {
|
||||||
wl_compositor::Request::CreateSurface { id } => {
|
wl_compositor::Request::CreateSurface { id } => {
|
||||||
|
@ -42,29 +49,24 @@ where
|
||||||
* wl_surface
|
* wl_surface
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type SurfaceImplemFn<R> =
|
type SurfaceImplemFn = dyn for<'a> FnMut(wl_surface::WlSurface, DispatchData<'a>);
|
||||||
dyn for<'a> FnMut(SurfaceEvent, wl_surface::WlSurface, CompositorToken<R>, DispatchData<'a>);
|
|
||||||
|
|
||||||
// Internal implementation data of surfaces
|
// Internal implementation data of surfaces
|
||||||
pub(crate) struct SurfaceImplem<R> {
|
pub(crate) struct SurfaceImplem {
|
||||||
log: ::slog::Logger,
|
log: ::slog::Logger,
|
||||||
implem: Rc<RefCell<SurfaceImplemFn<R>>>,
|
implem: Rc<RefCell<SurfaceImplemFn>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> SurfaceImplem<R> {
|
impl SurfaceImplem {
|
||||||
fn make<Impl>(log: ::slog::Logger, implem: Rc<RefCell<Impl>>) -> SurfaceImplem<R>
|
fn make<Impl>(log: ::slog::Logger, implem: Rc<RefCell<Impl>>) -> SurfaceImplem
|
||||||
where
|
where
|
||||||
Impl: for<'a> FnMut(SurfaceEvent, wl_surface::WlSurface, CompositorToken<R>, DispatchData<'a>)
|
Impl: for<'a> FnMut(wl_surface::WlSurface, DispatchData<'a>) + 'static,
|
||||||
+ 'static,
|
|
||||||
{
|
{
|
||||||
SurfaceImplem { log, implem }
|
SurfaceImplem { log, implem }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> SurfaceImplem<R>
|
impl SurfaceImplem {
|
||||||
where
|
|
||||||
R: 'static,
|
|
||||||
{
|
|
||||||
fn receive_surface_request(
|
fn receive_surface_request(
|
||||||
&mut self,
|
&mut self,
|
||||||
req: wl_surface::Request,
|
req: wl_surface::Request,
|
||||||
|
@ -73,8 +75,8 @@ where
|
||||||
) {
|
) {
|
||||||
match req {
|
match req {
|
||||||
wl_surface::Request::Attach { buffer, x, y } => {
|
wl_surface::Request::Attach { buffer, x, y } => {
|
||||||
SurfaceData::<R>::with_data(&surface, |d| {
|
PrivateSurfaceData::with_states(&surface, |states| {
|
||||||
d.buffer = Some(match buffer {
|
states.cached_state.pending::<SurfaceAttributes>().buffer = Some(match buffer {
|
||||||
Some(buffer) => BufferAssignment::NewBuffer {
|
Some(buffer) => BufferAssignment::NewBuffer {
|
||||||
buffer,
|
buffer,
|
||||||
delta: (x, y),
|
delta: (x, y),
|
||||||
|
@ -84,13 +86,21 @@ where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
wl_surface::Request::Damage { x, y, width, height } => {
|
wl_surface::Request::Damage { x, y, width, height } => {
|
||||||
SurfaceData::<R>::with_data(&surface, |d| {
|
PrivateSurfaceData::with_states(&surface, |states| {
|
||||||
d.damage.push(Damage::Surface(Rectangle { x, y, width, height }));
|
states
|
||||||
|
.cached_state
|
||||||
|
.pending::<SurfaceAttributes>()
|
||||||
|
.damage
|
||||||
|
.push(Damage::Surface(Rectangle { x, y, width, height }));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
wl_surface::Request::Frame { callback } => {
|
wl_surface::Request::Frame { callback } => {
|
||||||
SurfaceData::<R>::with_data(&surface, move |d| {
|
PrivateSurfaceData::with_states(&surface, |states| {
|
||||||
d.frame_callbacks.push((*callback).clone());
|
states
|
||||||
|
.cached_state
|
||||||
|
.pending::<SurfaceAttributes>()
|
||||||
|
.frame_callbacks
|
||||||
|
.push((*callback).clone());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
wl_surface::Request::SetOpaqueRegion { region } => {
|
wl_surface::Request::SetOpaqueRegion { region } => {
|
||||||
|
@ -98,29 +108,50 @@ where
|
||||||
let attributes_mutex = r.as_ref().user_data().get::<Mutex<RegionAttributes>>().unwrap();
|
let attributes_mutex = r.as_ref().user_data().get::<Mutex<RegionAttributes>>().unwrap();
|
||||||
attributes_mutex.lock().unwrap().clone()
|
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 } => {
|
wl_surface::Request::SetInputRegion { region } => {
|
||||||
let attributes = region.map(|r| {
|
let attributes = region.map(|r| {
|
||||||
let attributes_mutex = r.as_ref().user_data().get::<Mutex<RegionAttributes>>().unwrap();
|
let attributes_mutex = r.as_ref().user_data().get::<Mutex<RegionAttributes>>().unwrap();
|
||||||
attributes_mutex.lock().unwrap().clone()
|
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 => {
|
wl_surface::Request::Commit => {
|
||||||
let mut user_impl = self.implem.borrow_mut();
|
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");
|
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 } => {
|
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 } => {
|
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 } => {
|
wl_surface::Request::DamageBuffer { x, y, width, height } => {
|
||||||
SurfaceData::<R>::with_data(&surface, |d| {
|
PrivateSurfaceData::with_states(&surface, |states| {
|
||||||
d.damage.push(Damage::Buffer(Rectangle { x, y, width, height }))
|
states
|
||||||
|
.cached_state
|
||||||
|
.pending::<SurfaceAttributes>()
|
||||||
|
.damage
|
||||||
|
.push(Damage::Buffer(Rectangle { x, y, width, height }))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
wl_surface::Request::Destroy => {
|
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>,
|
surface: Main<wl_surface::WlSurface>,
|
||||||
log: ::slog::Logger,
|
log: ::slog::Logger,
|
||||||
implem: Rc<RefCell<Impl>>,
|
implem: Rc<RefCell<Impl>>,
|
||||||
) -> wl_surface::WlSurface
|
) -> wl_surface::WlSurface
|
||||||
where
|
where
|
||||||
R: Default + Send + 'static,
|
Impl: for<'a> FnMut(wl_surface::WlSurface, DispatchData<'a>) + 'static,
|
||||||
Impl: for<'a> FnMut(SurfaceEvent, wl_surface::WlSurface, CompositorToken<R>, DispatchData<'a>) + 'static,
|
|
||||||
{
|
{
|
||||||
surface.quick_assign({
|
surface.quick_assign({
|
||||||
let mut implem = SurfaceImplem::make(log, implem);
|
let mut implem = SurfaceImplem::make(log, implem);
|
||||||
move |surface, req, ddata| implem.receive_surface_request(req, surface.deref().clone(), ddata)
|
move |surface, req, ddata| implem.receive_surface_request(req, surface.deref().clone(), ddata)
|
||||||
});
|
});
|
||||||
surface.assign_destructor(Filter::new(|surface, _, _| SurfaceData::<R>::cleanup(&surface)));
|
surface.assign_destructor(Filter::new(|surface, _, _| PrivateSurfaceData::cleanup(&surface)));
|
||||||
surface.as_ref().user_data().set_threadsafe(SurfaceData::<R>::new);
|
surface
|
||||||
SurfaceData::<R>::init(&surface);
|
.as_ref()
|
||||||
|
.user_data()
|
||||||
|
.set_threadsafe(PrivateSurfaceData::new);
|
||||||
|
PrivateSurfaceData::init(&surface);
|
||||||
surface.deref().clone()
|
surface.deref().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,22 +250,19 @@ fn implement_region(region: Main<wl_region::WlRegion>) -> wl_region::WlRegion {
|
||||||
* wl_subcompositor
|
* wl_subcompositor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub(crate) fn implement_subcompositor<R>(
|
pub(crate) fn implement_subcompositor(
|
||||||
subcompositor: Main<wl_subcompositor::WlSubcompositor>,
|
subcompositor: Main<wl_subcompositor::WlSubcompositor>,
|
||||||
) -> wl_subcompositor::WlSubcompositor
|
) -> wl_subcompositor::WlSubcompositor {
|
||||||
where
|
|
||||||
R: RoleType + Role<SubsurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
subcompositor.quick_assign(move |subcompositor, request, _| match request {
|
subcompositor.quick_assign(move |subcompositor, request, _| match request {
|
||||||
wl_subcompositor::Request::GetSubsurface { id, surface, parent } => {
|
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(
|
subcompositor.as_ref().post_error(
|
||||||
wl_subcompositor::Error::BadSurface as u32,
|
wl_subcompositor::Error::BadSurface as u32,
|
||||||
"Surface already has a role.".into(),
|
"Surface already has a role.".into(),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
implement_subsurface::<R>(id, surface);
|
implement_subsurface(id, surface);
|
||||||
}
|
}
|
||||||
wl_subcompositor::Request::Destroy => {}
|
wl_subcompositor::Request::Destroy => {}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
@ -215,32 +274,72 @@ where
|
||||||
* wl_subsurface
|
* wl_subsurface
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fn with_subsurface_attributes<R, F>(subsurface: &wl_subsurface::WlSubsurface, f: F)
|
/// The cached state associated with a subsurface
|
||||||
where
|
pub struct SubsurfaceCachedState {
|
||||||
F: FnOnce(&mut SubsurfaceRole),
|
/// Location of the top-left corner of this subsurface
|
||||||
R: RoleType + Role<SubsurfaceRole> + 'static,
|
/// relative to its parent coordinate space
|
||||||
{
|
pub location: (i32, i32),
|
||||||
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?!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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>,
|
subsurface: Main<wl_subsurface::WlSubsurface>,
|
||||||
surface: wl_surface::WlSurface,
|
surface: wl_surface::WlSurface,
|
||||||
) -> wl_subsurface::WlSubsurface
|
) -> wl_subsurface::WlSubsurface {
|
||||||
where
|
let data_surface = surface.clone();
|
||||||
R: RoleType + Role<SubsurfaceRole> + 'static,
|
subsurface.quick_assign(move |subsurface, request, _| {
|
||||||
{
|
|
||||||
subsurface.quick_assign(|subsurface, request, _| {
|
|
||||||
match request {
|
match request {
|
||||||
wl_subsurface::Request::SetPosition { x, y } => {
|
wl_subsurface::Request::SetPosition { x, y } => {
|
||||||
with_subsurface_attributes::<R, _>(&subsurface, |attrs| {
|
PrivateSurfaceData::with_states(&surface, |state| {
|
||||||
attrs.location = (x, y);
|
state.cached_state.pending::<SubsurfaceCachedState>().location = (x, y);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
wl_subsurface::Request::PlaceAbove { sibling } => {
|
wl_subsurface::Request::PlaceAbove { sibling } => {
|
||||||
|
@ -249,7 +348,7 @@ where
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<wl_surface::WlSurface>()
|
.get::<wl_surface::WlSurface>()
|
||||||
.unwrap();
|
.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(
|
subsurface.as_ref().post_error(
|
||||||
wl_subsurface::Error::BadSurface as u32,
|
wl_subsurface::Error::BadSurface as u32,
|
||||||
"Provided surface is not a sibling or parent.".into(),
|
"Provided surface is not a sibling or parent.".into(),
|
||||||
|
@ -262,18 +361,28 @@ where
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<wl_surface::WlSurface>()
|
.get::<wl_surface::WlSurface>()
|
||||||
.unwrap();
|
.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(
|
subsurface.as_ref().post_error(
|
||||||
wl_subsurface::Error::BadSurface as u32,
|
wl_subsurface::Error::BadSurface as u32,
|
||||||
"Provided surface is not a sibling or parent.".into(),
|
"Provided surface is not a sibling or parent.".into(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wl_subsurface::Request::SetSync => with_subsurface_attributes::<R, _>(&subsurface, |attrs| {
|
wl_subsurface::Request::SetSync => PrivateSurfaceData::with_states(&surface, |state| {
|
||||||
attrs.sync = true;
|
state
|
||||||
|
.data_map
|
||||||
|
.get::<SubsurfaceState>()
|
||||||
|
.unwrap()
|
||||||
|
.sync
|
||||||
|
.store(true, Ordering::Release);
|
||||||
}),
|
}),
|
||||||
wl_subsurface::Request::SetDesync => with_subsurface_attributes::<R, _>(&subsurface, |attrs| {
|
wl_subsurface::Request::SetDesync => PrivateSurfaceData::with_states(&surface, |state| {
|
||||||
attrs.sync = false;
|
state
|
||||||
|
.data_map
|
||||||
|
.get::<SubsurfaceState>()
|
||||||
|
.unwrap()
|
||||||
|
.sync
|
||||||
|
.store(false, Ordering::Release);
|
||||||
}),
|
}),
|
||||||
wl_subsurface::Request::Destroy => {
|
wl_subsurface::Request::Destroy => {
|
||||||
// Our destructor already handles it
|
// Our destructor already handles it
|
||||||
|
@ -281,23 +390,25 @@ where
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
subsurface.assign_destructor(Filter::new(|subsurface, _, _| {
|
super::with_states(&data_surface, |states| {
|
||||||
destroy_subsurface::<R>(&subsurface)
|
states.data_map.insert_if_missing_threadsafe(SubsurfaceState::new)
|
||||||
}));
|
})
|
||||||
subsurface.as_ref().user_data().set_threadsafe(|| surface);
|
.unwrap();
|
||||||
|
subsurface.assign_destructor(Filter::new(|subsurface, _, _| destroy_subsurface(&subsurface)));
|
||||||
|
subsurface
|
||||||
|
.as_ref()
|
||||||
|
.user_data()
|
||||||
|
.set_threadsafe(move || data_surface);
|
||||||
subsurface.deref().clone()
|
subsurface.deref().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn destroy_subsurface<R>(subsurface: &wl_subsurface::WlSubsurface)
|
fn destroy_subsurface(subsurface: &wl_subsurface::WlSubsurface) {
|
||||||
where
|
|
||||||
R: RoleType + Role<SubsurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let surface = subsurface
|
let surface = subsurface
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<wl_surface::WlSurface>()
|
.get::<wl_surface::WlSurface>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if surface.as_ref().is_alive() {
|
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
|
//! 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
|
//! 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,
|
//! 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
|
//! you can iterate over the whole tree of subsurfaces to recover all the metadata you
|
||||||
|
@ -32,54 +33,67 @@
|
||||||
//! # #[macro_use] extern crate smithay;
|
//! # #[macro_use] extern crate smithay;
|
||||||
//! use smithay::wayland::compositor::compositor_init;
|
//! use smithay::wayland::compositor::compositor_init;
|
||||||
//!
|
//!
|
||||||
//! // Declare the roles enum
|
|
||||||
//! define_roles!(MyRoles);
|
|
||||||
//!
|
|
||||||
//! # let mut display = wayland_server::Display::new();
|
//! # let mut display = wayland_server::Display::new();
|
||||||
//! // Call the init function:
|
//! // Call the init function:
|
||||||
//! let (token, _, _) = compositor_init::<MyRoles, _, _>(
|
//! compositor_init(
|
||||||
//! &mut display,
|
//! &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
|
//! None // put a logger here
|
||||||
//! );
|
//! );
|
||||||
//!
|
//!
|
||||||
//! // this `token` is what you'll use to access the surface associated data
|
|
||||||
//!
|
|
||||||
//! // You're now ready to go!
|
//! // 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 main access to surface states is done through the [`with_states`] function, which
|
||||||
//! the `init` function. This token is necessary to retrieve the metadata associated with
|
//! gives you access to the [`SurfaceData`] instance associated with this surface. It acts
|
||||||
//! a surface. It can be cloned. See [`CompositorToken`]
|
//! as a general purpose container for associating state to a surface, double-buffered or
|
||||||
//! for the details of what it enables you.
|
//! not. See its documentation for more details.
|
||||||
//!
|
//!
|
||||||
//! The surface metadata is held in the [`SurfaceAttributes`]
|
//! ### State application and hooks
|
||||||
//! 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.
|
|
||||||
//!
|
//!
|
||||||
//! This [`CompositorToken`] also provides access to the metadata associated with the role of the
|
//! On commit of a surface several steps are taken to update the state of the surface. Actions
|
||||||
//! surfaces. See the documentation of the [`roles`] submodule
|
//! are taken by smithay in the following order:
|
||||||
//! for a detailed explanation.
|
//!
|
||||||
|
//! 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;
|
mod handlers;
|
||||||
pub mod roles;
|
mod transaction;
|
||||||
mod tree;
|
mod tree;
|
||||||
|
|
||||||
pub use self::tree::TraversalAction;
|
pub use self::cache::{Cacheable, MultiCache};
|
||||||
use self::{
|
pub use self::handlers::SubsurfaceCachedState;
|
||||||
roles::{AlreadyHasRole, Role, RoleType, WrongRole},
|
use self::tree::PrivateSurfaceData;
|
||||||
tree::SurfaceData,
|
pub use self::tree::{AlreadyHasRole, TraversalAction};
|
||||||
};
|
use crate::utils::{DeadResource, Rectangle};
|
||||||
use crate::utils::Rectangle;
|
|
||||||
use wayland_server::{
|
use wayland_server::{
|
||||||
protocol::{
|
protocol::{
|
||||||
wl_buffer, wl_callback, wl_compositor, wl_output, wl_region, wl_subcompositor, wl_surface::WlSurface,
|
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>,
|
_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
|
/// New buffer assignation for a surface
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum BufferAssignment {
|
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
|
/// The fields `buffer`, `damage` and `frame_callbacks` should be
|
||||||
/// should only be applied once a [`commit`](SurfaceEvent::Commit)
|
/// reset (by clearing their contents) once you have adequately
|
||||||
/// request is received from the surface.
|
/// processed them, as their contents are aggregated from commit to commit.
|
||||||
///
|
#[derive(Debug)]
|
||||||
/// You are responsible for setting those values as you see fit to avoid
|
|
||||||
/// processing them two times.
|
|
||||||
pub struct SurfaceAttributes {
|
pub struct SurfaceAttributes {
|
||||||
/// Buffer defining the contents of the surface
|
/// 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
|
/// An example possibility would be to trigger it once the frame
|
||||||
/// associated with this commit has been displayed on the screen.
|
/// associated with this commit has been displayed on the screen.
|
||||||
pub frame_callbacks: Vec<wl_callback::WlCallback>,
|
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 {
|
impl Default for SurfaceAttributes {
|
||||||
|
@ -204,7 +222,6 @@ impl Default for SurfaceAttributes {
|
||||||
input_region: None,
|
input_region: None,
|
||||||
damage: Vec::new(),
|
damage: Vec::new(),
|
||||||
frame_callbacks: 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
|
/// You provide three closures, a "filter", a "processor" and a "post filter".
|
||||||
/// access data associated with the [`wl_surface`](wayland_server::protocol::wl_surface)
|
///
|
||||||
/// and [`wl_region`](wayland_server::protocol::wl_region) managed
|
/// The first closure is initially called on a surface to determine if its children
|
||||||
/// by the `CompositorGlobal` that provided it.
|
/// should be processed as well. It returns a `TraversalAction<T>` reflecting that.
|
||||||
#[derive(Debug)]
|
///
|
||||||
pub struct CompositorToken<R> {
|
/// The second closure is supposed to do the actual processing. The processing closure for
|
||||||
_role: ::std::marker::PhantomData<*mut R>,
|
/// 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.
|
||||||
// we implement them manually because #[derive(..)] would require R: Clone
|
///
|
||||||
impl<R> Copy for CompositorToken<R> {}
|
/// The third closure is called once all the subtree of a node has been processed, and gives
|
||||||
impl<R> Clone for CompositorToken<R> {
|
/// an opportunity for early-stopping. If it returns `true` the processing will continue,
|
||||||
fn clone(&self) -> CompositorToken<R> {
|
/// while if it returns `false` it'll stop.
|
||||||
*self
|
///
|
||||||
}
|
/// The arguments provided to the closures are, in this order:
|
||||||
}
|
///
|
||||||
|
/// - The surface object itself
|
||||||
unsafe impl<R> Send for CompositorToken<R> {}
|
/// - a mutable reference to its surface attribute data
|
||||||
unsafe impl<R> Sync for CompositorToken<R> {}
|
/// - 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
|
||||||
impl<R> CompositorToken<R> {
|
/// to its children. See [`TraversalAction`] for details.
|
||||||
pub(crate) fn make() -> CompositorToken<R> {
|
///
|
||||||
CompositorToken {
|
/// If the surface not managed by the `CompositorGlobal` that provided this token, this
|
||||||
_role: ::std::marker::PhantomData,
|
/// 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,
|
||||||
impl<R: 'static> CompositorToken<R> {
|
processor: F2,
|
||||||
/// Access the data of a surface
|
post_filter: F3,
|
||||||
///
|
) where
|
||||||
/// The closure will be called with the contents of the data associated with this surface.
|
F1: FnMut(&WlSurface, &SurfaceData, &T) -> TraversalAction<T>,
|
||||||
///
|
F2: FnMut(&WlSurface, &SurfaceData, &T),
|
||||||
/// If the surface is not managed by the `CompositorGlobal` that provided this token, this
|
F3: FnMut(&WlSurface, &SurfaceData, &T) -> bool,
|
||||||
/// 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,
|
|
||||||
{
|
{
|
||||||
/// Access the data of a surface tree from bottom to top
|
PrivateSurfaceData::map_tree(surface, &initial, filter, processor, post_filter, false);
|
||||||
///
|
|
||||||
/// 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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: RoleType + 'static> CompositorToken<R> {
|
/// Access the data of a surface tree from top to bottom
|
||||||
/// Check whether this surface as a role or not
|
///
|
||||||
///
|
/// Behavior is the same as [`with_surface_tree_upward`], but the processing is done in the reverse order,
|
||||||
/// If the surface is not managed by the `CompositorGlobal` that provided this token, this
|
/// from the nearest of the screen to the deepest.
|
||||||
/// will panic (having more than one compositor is not supported).
|
///
|
||||||
pub fn has_a_role(self, surface: &WlSurface) -> bool {
|
/// This would typically be used to find out which surface of a subsurface tree has been clicked for example.
|
||||||
SurfaceData::<R>::has_a_role(surface)
|
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
|
/// Retrieve the parent of this surface
|
||||||
///
|
///
|
||||||
/// If the surface is not managed by the `CompositorGlobal` that provided this token, this
|
/// Returns `None` is this surface is a root surface
|
||||||
/// will panic (having more than one compositor is not supported).
|
pub fn get_parent(surface: &WlSurface) -> Option<WlSurface> {
|
||||||
pub fn has_role<RoleData>(self, surface: &WlSurface) -> bool
|
if !surface.as_ref().is_alive() {
|
||||||
where
|
return None;
|
||||||
R: Role<RoleData>,
|
|
||||||
{
|
|
||||||
SurfaceData::<R>::has_role::<RoleData>(surface)
|
|
||||||
}
|
}
|
||||||
|
PrivateSurfaceData::get_parent(surface)
|
||||||
|
}
|
||||||
|
|
||||||
/// Register that this surface has given role with default data
|
/// Retrieve the children of this surface
|
||||||
///
|
pub fn get_children(surface: &WlSurface) -> Vec<WlSurface> {
|
||||||
/// Fails if the surface already has a role.
|
if !surface.as_ref().is_alive() {
|
||||||
///
|
return Vec::new();
|
||||||
/// 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)
|
|
||||||
}
|
}
|
||||||
|
PrivateSurfaceData::get_children(surface)
|
||||||
|
}
|
||||||
|
|
||||||
/// Register that this surface has given role with given data
|
/// Check if this subsurface is a synchronized subsurface
|
||||||
///
|
///
|
||||||
/// Fails if the surface already has a role and returns the data.
|
/// Returns false if the surface is already dead
|
||||||
///
|
pub fn is_sync_subsurface(surface: &WlSurface) -> bool {
|
||||||
/// If the surface is not managed by the `CompositorGlobal` that provided this token, this
|
if !surface.as_ref().is_alive() {
|
||||||
/// will panic (having more than one compositor is not supported).
|
return false;
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
self::handlers::is_effectively_sync(surface)
|
||||||
|
}
|
||||||
|
|
||||||
/// Access the role data of a surface
|
/// Get the current role of this surface
|
||||||
///
|
pub fn get_role(surface: &WlSurface) -> Option<&'static str> {
|
||||||
/// Fails and don't call the closure if the surface doesn't have this role
|
if !surface.as_ref().is_alive() {
|
||||||
///
|
return None;
|
||||||
/// 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)
|
|
||||||
}
|
}
|
||||||
|
PrivateSurfaceData::get_role(surface)
|
||||||
|
}
|
||||||
|
|
||||||
/// Register that this surface does not have a role any longer and retrieve the data
|
/// Register that this surface has given role
|
||||||
///
|
///
|
||||||
/// Fails if the surface didn't already have this role.
|
/// Fails if the surface already has a role.
|
||||||
///
|
pub fn give_role(surface: &WlSurface, role: &'static str) -> Result<(), AlreadyHasRole> {
|
||||||
/// If the surface is not managed by the `CompositorGlobal` that provided this token, this
|
if !surface.as_ref().is_alive() {
|
||||||
/// will panic (having more than one compositor is not supported).
|
return Ok(());
|
||||||
pub fn remove_role<RoleData>(self, surface: &WlSurface) -> Result<RoleData, WrongRole>
|
|
||||||
where
|
|
||||||
R: Role<RoleData>,
|
|
||||||
{
|
|
||||||
SurfaceData::<R>::remove_role::<RoleData>(surface)
|
|
||||||
}
|
}
|
||||||
|
PrivateSurfaceData::set_role(surface, role)
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieve the metadata associated with a `wl_region`
|
/// Access the states associated to this surface
|
||||||
///
|
pub fn with_states<F, T>(surface: &WlSurface, f: F) -> Result<T, DeadResource>
|
||||||
/// If the region is not managed by the `CompositorGlobal` that provided this token, this
|
where
|
||||||
/// will panic (having more than one compositor is not supported).
|
F: FnOnce(&SurfaceData) -> T,
|
||||||
pub fn get_region_attributes(self, region: &wl_region::WlRegion) -> RegionAttributes {
|
{
|
||||||
|
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>>() {
|
match region.as_ref().user_data().get::<Mutex<RegionAttributes>>() {
|
||||||
Some(mutex) => mutex.lock().unwrap().clone(),
|
Some(mutex) => mutex.lock().unwrap().clone(),
|
||||||
None => panic!("Accessing the data of foreign regions is not supported."),
|
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)
|
/// Create new [`wl_compositor`](wayland_server::protocol::wl_compositor)
|
||||||
/// and [`wl_subcompositor`](wayland_server::protocol::wl_subcompositor) globals.
|
/// and [`wl_subcompositor`](wayland_server::protocol::wl_subcompositor) globals.
|
||||||
///
|
///
|
||||||
/// The globals are directly registered into the event loop, and this function
|
/// It returns the two global handles, in case you wish to remove these globals from
|
||||||
/// returns a [`CompositorToken`] which you'll need access the data associated to
|
/// the event loop in the future.
|
||||||
/// the [`wl_surface`](wayland_server::protocol::wl_surface)s.
|
pub fn compositor_init<Impl, L>(
|
||||||
///
|
|
||||||
/// 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>(
|
|
||||||
display: &mut Display,
|
display: &mut Display,
|
||||||
implem: Impl,
|
implem: Impl,
|
||||||
logger: L,
|
logger: L,
|
||||||
) -> (
|
) -> (
|
||||||
CompositorToken<R>,
|
|
||||||
Global<wl_compositor::WlCompositor>,
|
Global<wl_compositor::WlCompositor>,
|
||||||
Global<wl_subcompositor::WlSubcompositor>,
|
Global<wl_subcompositor::WlSubcompositor>,
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
R: Default + RoleType + Role<SubsurfaceRole> + Send + 'static,
|
Impl: for<'a> FnMut(WlSurface, DispatchData<'a>) + 'static,
|
||||||
Impl: for<'a> FnMut(SurfaceEvent, WlSurface, CompositorToken<R>, DispatchData<'a>) + 'static,
|
|
||||||
{
|
{
|
||||||
let log = crate::slog_or_fallback(logger).new(o!("smithay_module" => "compositor_handler"));
|
let log = crate::slog_or_fallback(logger).new(o!("smithay_module" => "compositor_handler"));
|
||||||
let implem = Rc::new(RefCell::new(implem));
|
let implem = Rc::new(RefCell::new(implem));
|
||||||
|
@ -522,32 +455,18 @@ where
|
||||||
let compositor = display.create_global(
|
let compositor = display.create_global(
|
||||||
4,
|
4,
|
||||||
Filter::new(move |(new_compositor, _version), _, _| {
|
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(
|
let subcompositor = display.create_global(
|
||||||
1,
|
1,
|
||||||
Filter::new(move |(new_subcompositor, _version), _, _| {
|
Filter::new(move |(new_subcompositor, _version), _, _| {
|
||||||
self::handlers::implement_subcompositor::<R>(new_subcompositor);
|
self::handlers::implement_subcompositor(new_subcompositor);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
(CompositorToken::make(), compositor, subcompositor)
|
(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,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[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 crate::wayland::Serial;
|
||||||
use std::sync::Mutex;
|
|
||||||
|
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;
|
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
|
/// Node of a subsurface tree, holding some user specified data type U
|
||||||
/// at each node
|
/// 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
|
/// Each node also appears within its children list, to allow relative placement
|
||||||
/// between them.
|
/// between them.
|
||||||
pub struct SurfaceData<R> {
|
pub struct PrivateSurfaceData {
|
||||||
parent: Option<WlSurface>,
|
parent: Option<WlSurface>,
|
||||||
children: Vec<WlSurface>,
|
children: Vec<WlSurface>,
|
||||||
role: R,
|
public_data: SurfaceData,
|
||||||
attributes: SurfaceAttributes,
|
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 {
|
pub enum Location {
|
||||||
Before,
|
Before,
|
||||||
After,
|
After,
|
||||||
|
@ -38,27 +63,28 @@ pub enum TraversalAction<T> {
|
||||||
Break,
|
Break,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Default> SurfaceData<R> {
|
impl PrivateSurfaceData {
|
||||||
pub fn new() -> Mutex<SurfaceData<R>> {
|
pub fn new() -> Mutex<PrivateSurfaceData> {
|
||||||
Mutex::new(SurfaceData {
|
Mutex::new(PrivateSurfaceData {
|
||||||
parent: None,
|
parent: None,
|
||||||
children: vec![],
|
children: vec![],
|
||||||
|
public_data: SurfaceData {
|
||||||
role: Default::default(),
|
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
|
/// Initializes the surface, must be called at creation for state coherence
|
||||||
pub fn init(surface: &WlSurface) {
|
pub fn init(surface: &WlSurface) {
|
||||||
let my_data_mutex = surface
|
let my_data_mutex = surface
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<Mutex<SurfaceData<R>>>()
|
.get::<Mutex<PrivateSurfaceData>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut my_data = my_data_mutex.lock().unwrap();
|
let mut my_data = my_data_mutex.lock().unwrap();
|
||||||
debug_assert!(my_data.children.is_empty());
|
debug_assert!(my_data.children.is_empty());
|
||||||
|
@ -70,7 +96,7 @@ where
|
||||||
let my_data_mutex = surface
|
let my_data_mutex = surface
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<Mutex<SurfaceData<R>>>()
|
.get::<Mutex<PrivateSurfaceData>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut my_data = my_data_mutex.lock().unwrap();
|
let mut my_data = my_data_mutex.lock().unwrap();
|
||||||
if let Some(old_parent) = my_data.parent.take() {
|
if let Some(old_parent) = my_data.parent.take() {
|
||||||
|
@ -78,7 +104,7 @@ where
|
||||||
let old_parent_mutex = old_parent
|
let old_parent_mutex = old_parent
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<Mutex<SurfaceData<R>>>()
|
.get::<Mutex<PrivateSurfaceData>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut old_parent_guard = old_parent_mutex.lock().unwrap();
|
let mut old_parent_guard = old_parent_mutex.lock().unwrap();
|
||||||
old_parent_guard
|
old_parent_guard
|
||||||
|
@ -87,7 +113,11 @@ where
|
||||||
}
|
}
|
||||||
// orphan all our children
|
// orphan all our children
|
||||||
for child in &my_data.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) {
|
if std::ptr::eq(child_mutex, my_data_mutex) {
|
||||||
// This child is ourselves, don't do anything.
|
// This child is ourselves, don't do anything.
|
||||||
continue;
|
continue;
|
||||||
|
@ -97,108 +127,119 @@ where
|
||||||
child_guard.parent = None;
|
child_guard.parent = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: RoleType + 'static> SurfaceData<R> {
|
pub fn set_role(surface: &WlSurface, role: &'static str) -> Result<(), AlreadyHasRole> {
|
||||||
pub fn has_a_role(surface: &WlSurface) -> bool {
|
let my_data_mutex = surface
|
||||||
debug_assert!(surface.as_ref().is_alive());
|
|
||||||
let data_mutex = surface
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<Mutex<SurfaceData<R>>>()
|
.get::<Mutex<PrivateSurfaceData>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let data_guard = data_mutex.lock().unwrap();
|
let mut my_data = my_data_mutex.lock().unwrap();
|
||||||
<R as RoleType>::has_role(&data_guard.role)
|
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 get_role(surface: &WlSurface) -> Option<&'static str> {
|
||||||
pub fn has_role<RoleData>(surface: &WlSurface) -> bool
|
let my_data_mutex = surface
|
||||||
where
|
|
||||||
R: Role<RoleData>,
|
|
||||||
{
|
|
||||||
debug_assert!(surface.as_ref().is_alive());
|
|
||||||
let data_mutex = surface
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<Mutex<SurfaceData<R>>>()
|
.get::<Mutex<PrivateSurfaceData>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let data_guard = data_mutex.lock().unwrap();
|
let my_data = my_data_mutex.lock().unwrap();
|
||||||
<R as Role<RoleData>>::has(&data_guard.role)
|
my_data.public_data.role
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register that this surface has a role, fails if it already has one
|
pub fn with_states<T, F: FnOnce(&SurfaceData) -> T>(surface: &WlSurface, f: F) -> T {
|
||||||
pub fn give_role<RoleData>(surface: &WlSurface) -> Result<(), AlreadyHasRole>
|
let my_data_mutex = surface
|
||||||
where
|
|
||||||
R: Role<RoleData>,
|
|
||||||
RoleData: Default,
|
|
||||||
{
|
|
||||||
debug_assert!(surface.as_ref().is_alive());
|
|
||||||
let data_mutex = surface
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<Mutex<SurfaceData<R>>>()
|
.get::<Mutex<PrivateSurfaceData>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut data_guard = data_mutex.lock().unwrap();
|
let my_data = my_data_mutex.lock().unwrap();
|
||||||
<R as Role<RoleData>>::set(&mut data_guard.role)
|
f(&my_data.public_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register that this surface has a role with given data
|
pub fn add_commit_hook(surface: &WlSurface, hook: fn(&WlSurface)) {
|
||||||
///
|
let my_data_mutex = surface
|
||||||
/// 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
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<Mutex<SurfaceData<R>>>()
|
.get::<Mutex<PrivateSurfaceData>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut data_guard = data_mutex.lock().unwrap();
|
let mut my_data = my_data_mutex.lock().unwrap();
|
||||||
<R as Role<RoleData>>::set_with(&mut data_guard.role, data)
|
my_data.commit_hooks.push(hook);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register that this surface has no role and returns the data
|
pub fn invoke_commit_hooks(surface: &WlSurface) {
|
||||||
///
|
// don't hold the mutex while the hooks are invoked
|
||||||
/// It is a noop if this surface already didn't have one, but fails if
|
let hooks = {
|
||||||
/// the role was "subsurface", it must be removed by the `unset_parent` method.
|
let my_data_mutex = surface
|
||||||
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
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<Mutex<SurfaceData<R>>>()
|
.get::<Mutex<PrivateSurfaceData>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut data_guard = data_mutex.lock().unwrap();
|
let my_data = my_data_mutex.lock().unwrap();
|
||||||
<R as Role<RoleData>>::unset(&mut data_guard.role)
|
my_data.commit_hooks.clone()
|
||||||
|
};
|
||||||
|
for hook in hooks {
|
||||||
|
hook(surface);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access to the role data
|
pub fn commit(surface: &WlSurface) {
|
||||||
pub fn with_role_data<RoleData, F, T>(surface: &WlSurface, f: F) -> Result<T, WrongRole>
|
let is_sync = is_effectively_sync(surface);
|
||||||
where
|
let children = get_children(surface);
|
||||||
R: Role<RoleData>,
|
let my_data_mutex = surface
|
||||||
F: FnOnce(&mut RoleData) -> T,
|
|
||||||
{
|
|
||||||
debug_assert!(surface.as_ref().is_alive());
|
|
||||||
let data_mutex = surface
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<Mutex<SurfaceData<R>>>()
|
.get::<Mutex<PrivateSurfaceData>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut data_guard = data_mutex.lock().unwrap();
|
let mut my_data = my_data_mutex.lock().unwrap();
|
||||||
let data = <R as Role<RoleData>>::data_mut(&mut data_guard.role)?;
|
// commit our state
|
||||||
Ok(f(data))
|
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
|
/// Checks if the first surface is an ancestor of the second
|
||||||
pub fn is_ancestor(a: &WlSurface, b: &WlSurface) -> bool {
|
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();
|
let b_guard = b_mutex.lock().unwrap();
|
||||||
if let Some(ref parent) = b_guard.parent {
|
if let Some(ref parent) = b_guard.parent {
|
||||||
if parent.as_ref().equals(a.as_ref()) {
|
if parent.as_ref().equals(a.as_ref()) {
|
||||||
|
@ -225,10 +266,18 @@ impl<R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<R> {
|
||||||
|
|
||||||
// change child's parent
|
// 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();
|
let mut child_guard = child_mutex.lock().unwrap();
|
||||||
// if surface already has a role, it cannot become a subsurface
|
// 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());
|
debug_assert!(child_guard.parent.is_none());
|
||||||
child_guard.parent = Some(parent.clone());
|
child_guard.parent = Some(parent.clone());
|
||||||
}
|
}
|
||||||
|
@ -237,7 +286,7 @@ impl<R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<R> {
|
||||||
let parent_mutex = parent
|
let parent_mutex = parent
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<Mutex<SurfaceData<R>>>()
|
.get::<Mutex<PrivateSurfaceData>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut parent_guard = parent_mutex.lock().unwrap();
|
let mut parent_guard = parent_mutex.lock().unwrap();
|
||||||
parent_guard.children.push(child.clone())
|
parent_guard.children.push(child.clone())
|
||||||
|
@ -251,22 +300,20 @@ impl<R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<R> {
|
||||||
pub fn unset_parent(child: &WlSurface) {
|
pub fn unset_parent(child: &WlSurface) {
|
||||||
debug_assert!(child.as_ref().is_alive());
|
debug_assert!(child.as_ref().is_alive());
|
||||||
let old_parent = {
|
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 mut child_guard = child_mutex.lock().unwrap();
|
||||||
let old_parent = child_guard.parent.take();
|
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
|
|
||||||
};
|
};
|
||||||
// unregister from our parent
|
// unregister from our parent
|
||||||
if let Some(old_parent) = old_parent {
|
if let Some(old_parent) = old_parent {
|
||||||
let parent_mutex = old_parent
|
let parent_mutex = old_parent
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<Mutex<SurfaceData<R>>>()
|
.get::<Mutex<PrivateSurfaceData>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut parent_guard = parent_mutex.lock().unwrap();
|
let mut parent_guard = parent_mutex.lock().unwrap();
|
||||||
parent_guard
|
parent_guard
|
||||||
|
@ -277,7 +324,11 @@ impl<R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<R> {
|
||||||
|
|
||||||
/// Retrieve the parent surface (if any) of this surface
|
/// Retrieve the parent surface (if any) of this surface
|
||||||
pub fn get_parent(child: &WlSurface) -> Option<WlSurface> {
|
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();
|
let child_guard = child_mutex.lock().unwrap();
|
||||||
child_guard.parent.as_ref().cloned()
|
child_guard.parent.as_ref().cloned()
|
||||||
}
|
}
|
||||||
|
@ -287,7 +338,7 @@ impl<R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<R> {
|
||||||
let parent_mutex = parent
|
let parent_mutex = parent
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<Mutex<SurfaceData<R>>>()
|
.get::<Mutex<PrivateSurfaceData>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let parent_guard = parent_mutex.lock().unwrap();
|
let parent_guard = parent_mutex.lock().unwrap();
|
||||||
parent_guard
|
parent_guard
|
||||||
|
@ -306,7 +357,7 @@ impl<R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<R> {
|
||||||
let data_mutex = surface
|
let data_mutex = surface
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<Mutex<SurfaceData<R>>>()
|
.get::<Mutex<PrivateSurfaceData>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let data_guard = data_mutex.lock().unwrap();
|
let data_guard = data_mutex.lock().unwrap();
|
||||||
data_guard.parent.as_ref().cloned().unwrap()
|
data_guard.parent.as_ref().cloned().unwrap()
|
||||||
|
@ -324,7 +375,7 @@ impl<R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<R> {
|
||||||
let parent_mutex = parent
|
let parent_mutex = parent
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<Mutex<SurfaceData<R>>>()
|
.get::<Mutex<PrivateSurfaceData>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut parent_guard = parent_mutex.lock().unwrap();
|
let mut parent_guard = parent_mutex.lock().unwrap();
|
||||||
let my_index = index_of(surface, &parent_guard.children).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> {
|
impl PrivateSurfaceData {
|
||||||
/// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Access sequentially the attributes associated with a surface tree,
|
/// Access sequentially the attributes associated with a surface tree,
|
||||||
/// in a depth-first order.
|
/// in a depth-first order.
|
||||||
///
|
///
|
||||||
|
@ -384,9 +418,9 @@ impl<R: 'static> SurfaceData<R> {
|
||||||
mut post_filter: F3,
|
mut post_filter: F3,
|
||||||
reverse: bool,
|
reverse: bool,
|
||||||
) where
|
) where
|
||||||
F1: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction<T>,
|
F1: FnMut(&WlSurface, &SurfaceData, &T) -> TraversalAction<T>,
|
||||||
F2: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T),
|
F2: FnMut(&WlSurface, &SurfaceData, &T),
|
||||||
F3: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T) -> bool,
|
F3: FnMut(&WlSurface, &SurfaceData, &T) -> bool,
|
||||||
{
|
{
|
||||||
Self::map(
|
Self::map(
|
||||||
surface,
|
surface,
|
||||||
|
@ -408,25 +442,25 @@ impl<R: 'static> SurfaceData<R> {
|
||||||
reverse: bool,
|
reverse: bool,
|
||||||
) -> bool
|
) -> bool
|
||||||
where
|
where
|
||||||
F1: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction<T>,
|
F1: FnMut(&WlSurface, &SurfaceData, &T) -> TraversalAction<T>,
|
||||||
F2: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T),
|
F2: FnMut(&WlSurface, &SurfaceData, &T),
|
||||||
F3: FnMut(&WlSurface, &mut SurfaceAttributes, &mut R, &T) -> bool,
|
F3: FnMut(&WlSurface, &SurfaceData, &T) -> bool,
|
||||||
{
|
{
|
||||||
let data_mutex = surface
|
let data_mutex = surface
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<Mutex<SurfaceData<R>>>()
|
.get::<Mutex<PrivateSurfaceData>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut data_guard = data_mutex.lock().unwrap();
|
let mut data_guard = data_mutex.lock().unwrap();
|
||||||
let data_guard = &mut *data_guard;
|
let data_guard = &mut *data_guard;
|
||||||
// call the filter on ourselves
|
// 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) => {
|
TraversalAction::DoChildren(t) => {
|
||||||
// loop over children
|
// loop over children
|
||||||
if reverse {
|
if reverse {
|
||||||
for c in data_guard.children.iter().rev() {
|
for c in data_guard.children.iter().rev() {
|
||||||
if c.as_ref().equals(surface.as_ref()) {
|
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) {
|
} else if !Self::map(c, &t, filter, processor, post_filter, true) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -434,17 +468,17 @@ impl<R: 'static> SurfaceData<R> {
|
||||||
} else {
|
} else {
|
||||||
for c in &data_guard.children {
|
for c in &data_guard.children {
|
||||||
if c.as_ref().equals(surface.as_ref()) {
|
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) {
|
} else if !Self::map(c, &t, filter, processor, post_filter, false) {
|
||||||
return 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 => {
|
TraversalAction::SkipChildren => {
|
||||||
// still process ourselves
|
// still process ourselves
|
||||||
processor(surface, &mut data_guard.attributes, &mut data_guard.role, initial);
|
processor(surface, &data_guard.public_data, initial);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
TraversalAction::Break => false,
|
TraversalAction::Break => false,
|
||||||
|
|
|
@ -6,14 +6,13 @@ use wayland_server::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::wayland::{
|
use crate::wayland::{
|
||||||
compositor::{roles::Role, CompositorToken},
|
|
||||||
seat::{AxisFrame, GrabStartData, PointerGrab, PointerInnerHandle, Seat},
|
seat::{AxisFrame, GrabStartData, PointerGrab, PointerInnerHandle, Seat},
|
||||||
Serial,
|
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,
|
start_data: GrabStartData,
|
||||||
data_source: Option<wl_data_source::WlDataSource>,
|
data_source: Option<wl_data_source::WlDataSource>,
|
||||||
current_focus: Option<wl_surface::WlSurface>,
|
current_focus: Option<wl_surface::WlSurface>,
|
||||||
|
@ -22,20 +21,18 @@ pub(crate) struct DnDGrab<R> {
|
||||||
icon: Option<wl_surface::WlSurface>,
|
icon: Option<wl_surface::WlSurface>,
|
||||||
origin: wl_surface::WlSurface,
|
origin: wl_surface::WlSurface,
|
||||||
callback: Rc<RefCell<dyn FnMut(super::DataDeviceEvent)>>,
|
callback: Rc<RefCell<dyn FnMut(super::DataDeviceEvent)>>,
|
||||||
token: CompositorToken<R>,
|
|
||||||
seat: Seat,
|
seat: Seat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Role<DnDIconRole> + 'static> DnDGrab<R> {
|
impl DnDGrab {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
start_data: GrabStartData,
|
start_data: GrabStartData,
|
||||||
source: Option<wl_data_source::WlDataSource>,
|
source: Option<wl_data_source::WlDataSource>,
|
||||||
origin: wl_surface::WlSurface,
|
origin: wl_surface::WlSurface,
|
||||||
seat: Seat,
|
seat: Seat,
|
||||||
icon: Option<wl_surface::WlSurface>,
|
icon: Option<wl_surface::WlSurface>,
|
||||||
token: CompositorToken<R>,
|
|
||||||
callback: Rc<RefCell<dyn FnMut(super::DataDeviceEvent)>>,
|
callback: Rc<RefCell<dyn FnMut(super::DataDeviceEvent)>>,
|
||||||
) -> DnDGrab<R> {
|
) -> DnDGrab {
|
||||||
DnDGrab {
|
DnDGrab {
|
||||||
start_data,
|
start_data,
|
||||||
data_source: source,
|
data_source: source,
|
||||||
|
@ -45,13 +42,12 @@ impl<R: Role<DnDIconRole> + 'static> DnDGrab<R> {
|
||||||
origin,
|
origin,
|
||||||
icon,
|
icon,
|
||||||
callback,
|
callback,
|
||||||
token,
|
|
||||||
seat,
|
seat,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Role<DnDIconRole> + 'static> PointerGrab for DnDGrab<R> {
|
impl PointerGrab for DnDGrab {
|
||||||
fn motion(
|
fn motion(
|
||||||
&mut self,
|
&mut self,
|
||||||
_handle: &mut PointerInnerHandle<'_>,
|
_handle: &mut PointerInnerHandle<'_>,
|
||||||
|
@ -211,11 +207,7 @@ impl<R: Role<DnDIconRole> + 'static> PointerGrab for DnDGrab<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(&mut *self.callback.borrow_mut())(super::DataDeviceEvent::DnDDropped);
|
(&mut *self.callback.borrow_mut())(super::DataDeviceEvent::DnDDropped);
|
||||||
if let Some(icon) = self.icon.take() {
|
self.icon = None;
|
||||||
if icon.as_ref().is_alive() {
|
|
||||||
self.token.remove_role::<super::DnDIconRole>(&icon).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// in all cases abandon the drop
|
// in all cases abandon the drop
|
||||||
// no more buttons are pressed, release the grab
|
// no more buttons are pressed, release the grab
|
||||||
handle.unset_grab(serial, time);
|
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
|
//! - 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.
|
//! 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
|
//! The module defines the role `"dnd_icon"` that is assigned to surfaces used as drag'n'drop icons.
|
||||||
//! represent surfaces that are used as a DnD icon.
|
|
||||||
//!
|
//!
|
||||||
//! ## Initialization
|
//! ## Initialization
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # extern crate wayland_server;
|
//! # extern crate wayland_server;
|
||||||
//! # #[macro_use] extern crate smithay;
|
|
||||||
//! use smithay::wayland::data_device::{init_data_device, default_action_chooser, DnDIconRole};
|
//! use smithay::wayland::data_device::{init_data_device, default_action_chooser, DnDIconRole};
|
||||||
//! # use smithay::wayland::compositor::compositor_init;
|
//! # 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 mut display = wayland_server::Display::new();
|
||||||
//! # let (compositor_token, _, _) = compositor_init::<Roles, _, _>(&mut display, |_, _, _, _| {}, None);
|
|
||||||
//! // init the data device:
|
//! // init the data device:
|
||||||
//! init_data_device(
|
//! init_data_device(
|
||||||
//! &mut display, // the display
|
//! &mut display, // the display
|
||||||
//! |dnd_event| { /* a callback to react to client DnD/selection actions */ },
|
//! |dnd_event| { /* a callback to react to client DnD/selection actions */ },
|
||||||
//! default_action_chooser, // a closure to choose the DnD action depending on clients
|
//! default_action_chooser, // a closure to choose the DnD action depending on clients
|
||||||
//! // negociation
|
//! // negociation
|
||||||
//! compositor_token.clone(), // a compositor token
|
|
||||||
//! None // insert a logger here
|
//! None // insert a logger here
|
||||||
//! );
|
//! );
|
||||||
//! ```
|
//! ```
|
||||||
|
@ -65,7 +57,7 @@ use wayland_server::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::wayland::{
|
use crate::wayland::{
|
||||||
compositor::{roles::Role, CompositorToken},
|
compositor,
|
||||||
seat::{GrabStartData, Seat},
|
seat::{GrabStartData, Seat},
|
||||||
Serial,
|
Serial,
|
||||||
};
|
};
|
||||||
|
@ -77,6 +69,8 @@ mod server_dnd_grab;
|
||||||
pub use self::data_source::{with_source_metadata, SourceMetadata};
|
pub use self::data_source::{with_source_metadata, SourceMetadata};
|
||||||
pub use self::server_dnd_grab::ServerDndEvent;
|
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
|
/// Events that are generated by interactions of the clients with the data device
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DataDeviceEvent {
|
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
|
/// 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
|
/// chosen (and thus the drag'n'drop should abort on drop), return
|
||||||
/// [`DndAction::empty()`](wayland_server::protocol::wl_data_device_manager::DndAction::empty).
|
/// [`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,
|
display: &mut Display,
|
||||||
callback: C,
|
callback: C,
|
||||||
action_choice: F,
|
action_choice: F,
|
||||||
token: CompositorToken<R>,
|
|
||||||
logger: L,
|
logger: L,
|
||||||
) -> Global<wl_data_device_manager::WlDataDeviceManager>
|
) -> Global<wl_data_device_manager::WlDataDeviceManager>
|
||||||
where
|
where
|
||||||
F: FnMut(DndAction, DndAction) -> DndAction + 'static,
|
F: FnMut(DndAction, DndAction) -> DndAction + 'static,
|
||||||
C: FnMut(DataDeviceEvent) + 'static,
|
C: FnMut(DataDeviceEvent) + 'static,
|
||||||
R: Role<DnDIconRole> + 'static,
|
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
let log = crate::slog_or_fallback(logger).new(o!("smithay_module" => "data_device_mgr"));
|
let log = crate::slog_or_fallback(logger).new(o!("smithay_module" => "data_device_mgr"));
|
||||||
|
@ -294,7 +286,7 @@ where
|
||||||
display.create_global(
|
display.create_global(
|
||||||
3,
|
3,
|
||||||
Filter::new(move |(ddm, _version), _, _| {
|
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>,
|
ddm: Main<wl_data_device_manager::WlDataDeviceManager>,
|
||||||
callback: Rc<RefCell<C>>,
|
callback: Rc<RefCell<C>>,
|
||||||
action_choice: Rc<RefCell<F>>,
|
action_choice: Rc<RefCell<F>>,
|
||||||
token: CompositorToken<R>,
|
|
||||||
log: ::slog::Logger,
|
log: ::slog::Logger,
|
||||||
) -> wl_data_device_manager::WlDataDeviceManager
|
) -> wl_data_device_manager::WlDataDeviceManager
|
||||||
where
|
where
|
||||||
F: FnMut(DndAction, DndAction) -> DndAction + 'static,
|
F: FnMut(DndAction, DndAction) -> DndAction + 'static,
|
||||||
C: FnMut(DataDeviceEvent) + 'static,
|
C: FnMut(DataDeviceEvent) + 'static,
|
||||||
R: Role<DnDIconRole> + 'static,
|
|
||||||
{
|
{
|
||||||
use self::wl_data_device_manager::Request;
|
use self::wl_data_device_manager::Request;
|
||||||
ddm.quick_assign(move |_ddm, req, _data| match req {
|
ddm.quick_assign(move |_ddm, req, _data| match req {
|
||||||
|
@ -399,7 +389,6 @@ where
|
||||||
seat.clone(),
|
seat.clone(),
|
||||||
callback.clone(),
|
callback.clone(),
|
||||||
action_choice.clone(),
|
action_choice.clone(),
|
||||||
token,
|
|
||||||
log.clone(),
|
log.clone(),
|
||||||
);
|
);
|
||||||
seat_data.borrow_mut().known_devices.push(data_device);
|
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>>,
|
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>,
|
dd: Main<wl_data_device::WlDataDevice>,
|
||||||
seat: Seat,
|
seat: Seat,
|
||||||
callback: Rc<RefCell<C>>,
|
callback: Rc<RefCell<C>>,
|
||||||
action_choice: Rc<RefCell<F>>,
|
action_choice: Rc<RefCell<F>>,
|
||||||
token: CompositorToken<R>,
|
|
||||||
log: ::slog::Logger,
|
log: ::slog::Logger,
|
||||||
) -> wl_data_device::WlDataDevice
|
) -> wl_data_device::WlDataDevice
|
||||||
where
|
where
|
||||||
F: FnMut(DndAction, DndAction) -> DndAction + 'static,
|
F: FnMut(DndAction, DndAction) -> DndAction + 'static,
|
||||||
C: FnMut(DataDeviceEvent) + 'static,
|
C: FnMut(DataDeviceEvent) + 'static,
|
||||||
R: Role<DnDIconRole> + 'static,
|
|
||||||
{
|
{
|
||||||
use self::wl_data_device::Request;
|
use self::wl_data_device::Request;
|
||||||
let dd_data = DataDeviceData {
|
let dd_data = DataDeviceData {
|
||||||
|
@ -449,7 +436,7 @@ where
|
||||||
if let Some(pointer) = seat.get_pointer() {
|
if let Some(pointer) = seat.get_pointer() {
|
||||||
if pointer.has_grab(serial) {
|
if pointer.has_grab(serial) {
|
||||||
if let Some(ref icon) = icon {
|
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(
|
dd.as_ref().post_error(
|
||||||
wl_data_device::Error::Role as u32,
|
wl_data_device::Error::Role as u32,
|
||||||
"Given surface already has an other role".into(),
|
"Given surface already has an other role".into(),
|
||||||
|
@ -470,7 +457,6 @@ where
|
||||||
origin,
|
origin,
|
||||||
seat.clone(),
|
seat.clone(),
|
||||||
icon,
|
icon,
|
||||||
token,
|
|
||||||
callback.clone(),
|
callback.clone(),
|
||||||
),
|
),
|
||||||
serial,
|
serial,
|
||||||
|
|
|
@ -265,7 +265,7 @@ where
|
||||||
format,
|
format,
|
||||||
DmabufFlags::from_bits_truncate(flags),
|
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() {
|
for (i, plane) in planes.into_iter().enumerate() {
|
||||||
let offset = plane.offset;
|
let offset = plane.offset;
|
||||||
let stride = plane.stride;
|
let stride = plane.stride;
|
||||||
|
@ -355,7 +355,7 @@ where
|
||||||
format,
|
format,
|
||||||
DmabufFlags::from_bits_truncate(flags),
|
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() {
|
for (i, plane) in planes.into_iter().enumerate() {
|
||||||
let offset = plane.offset;
|
let offset = plane.offset;
|
||||||
let stride = plane.stride;
|
let stride = plane.stride;
|
||||||
|
|
|
@ -21,60 +21,32 @@
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # extern crate wayland_server;
|
//! # extern crate wayland_server;
|
||||||
//! # #[macro_use] extern crate smithay;
|
|
||||||
//! #
|
|
||||||
//! # use smithay::wayland::compositor::roles::*;
|
|
||||||
//! # use smithay::wayland::compositor::CompositorToken;
|
|
||||||
//! use smithay::wayland::explicit_synchronization::*;
|
//! use smithay::wayland::explicit_synchronization::*;
|
||||||
//! # define_roles!(MyRoles);
|
|
||||||
//! #
|
|
||||||
//! # let mut display = wayland_server::Display::new();
|
//! # let mut display = wayland_server::Display::new();
|
||||||
//! # let (compositor_token, _, _) = smithay::wayland::compositor::compositor_init::<MyRoles, _, _>(
|
|
||||||
//! # &mut display,
|
|
||||||
//! # |_, _, _, _| {},
|
|
||||||
//! # None
|
|
||||||
//! # );
|
|
||||||
//! init_explicit_synchronization_global(
|
//! init_explicit_synchronization_global(
|
||||||
//! &mut display,
|
//! &mut display,
|
||||||
//! compositor_token,
|
|
||||||
//! None /* You can insert a logger here */
|
//! None /* You can insert a logger here */
|
||||||
//! );
|
//! );
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Then when handling a surface commit, you can retrieve the synchronization information for the surface
|
//! Then when handling a surface commit, you can retrieve the synchronization information for the surface states:
|
||||||
//! data:
|
//! ```
|
||||||
//! ```no_run
|
|
||||||
//! # extern crate wayland_server;
|
//! # extern crate wayland_server;
|
||||||
//! # #[macro_use] extern crate smithay;
|
//! # #[macro_use] extern crate smithay;
|
||||||
//! #
|
//! #
|
||||||
//! # use wayland_server::protocol::wl_surface::WlSurface;
|
//! # use wayland_server::protocol::wl_surface::WlSurface;
|
||||||
//! # use smithay::wayland::compositor::CompositorToken;
|
|
||||||
//! # use smithay::wayland::explicit_synchronization::*;
|
//! # use smithay::wayland::explicit_synchronization::*;
|
||||||
//! #
|
//! #
|
||||||
//! # fn dummy_function<R: 'static>(surface: &WlSurface, compositor_token: CompositorToken<R>) {
|
//! # fn dummy_function<R: 'static>(surface: &WlSurface) {
|
||||||
//! compositor_token.with_surface_data(&surface, |surface_attributes| {
|
//! use smithay::wayland::compositor::with_states;
|
||||||
//! // While you retrieve the surface data from the commit ...
|
//! with_states(&surface, |states| {
|
||||||
//! // Check the explicit synchronization data:
|
//! let explicit_sync_state = states.cached_state.current::<ExplicitSyncState>();
|
||||||
//! match get_explicit_synchronization_state(surface_attributes) {
|
//! /* process the explicit_sync_state */
|
||||||
//! 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
|
|
||||||
//! */
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! });
|
//! });
|
||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use std::{
|
use std::{cell::RefCell, ops::Deref as _, os::unix::io::RawFd};
|
||||||
cell::RefCell,
|
|
||||||
ops::{Deref as _, DerefMut as _},
|
|
||||||
os::unix::io::RawFd,
|
|
||||||
};
|
|
||||||
|
|
||||||
use wayland_protocols::unstable::linux_explicit_synchronization::v1::server::{
|
use wayland_protocols::unstable::linux_explicit_synchronization::v1::server::{
|
||||||
zwp_linux_buffer_release_v1::ZwpLinuxBufferReleaseV1,
|
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 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
|
/// An object to signal end of use of a buffer
|
||||||
#[derive(Debug)]
|
#[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
|
/// 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
|
/// 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`).
|
/// 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)]
|
#[derive(Debug)]
|
||||||
pub struct ExplicitSyncState {
|
pub struct ExplicitSyncState {
|
||||||
/// An acquire `dma_fence` object, that you should wait on before accessing the contents of the
|
/// 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>,
|
pub release: Option<ExplicitBufferRelease>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InternalState {
|
impl Default for ExplicitSyncState {
|
||||||
sync_state: ExplicitSyncState,
|
fn default() -> Self {
|
||||||
sync_resource: ZwpLinuxSurfaceSynchronizationV1,
|
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 {
|
struct ESUserData {
|
||||||
state: RefCell<Option<InternalState>>,
|
state: RefCell<Option<ZwpLinuxSurfaceSynchronizationV1>>,
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Possible errors you can send to an ill-behaving clients
|
/// 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 {}
|
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
|
/// Send a synchronization error to a client
|
||||||
///
|
///
|
||||||
/// See the enum definition for possible errors. These errors are protocol errors, meaning that
|
/// 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
|
/// the client associated with this `SurfaceAttributes` will be killed as a result of calling this
|
||||||
/// function.
|
/// function.
|
||||||
pub fn send_explicit_synchronization_error(attrs: &SurfaceAttributes, error: ExplicitSyncError) {
|
pub fn send_explicit_synchronization_error(attrs: &SurfaceData, error: ExplicitSyncError) {
|
||||||
if let Some(ref data) = attrs.user_data.get::<ESUserData>() {
|
if let Some(ref data) = attrs.data_map.get::<ESUserData>() {
|
||||||
if let Some(state) = data.state.borrow().deref() {
|
if let Some(sync_resource) = data.state.borrow().deref() {
|
||||||
match error {
|
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,
|
zwp_linux_surface_synchronization_v1::Error::InvalidFence as u32,
|
||||||
"The fence specified by the client could not be imported.".into(),
|
"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,
|
zwp_linux_surface_synchronization_v1::Error::UnsupportedBuffer as u32,
|
||||||
"The buffer does not support explicit synchronization.".into(),
|
"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,
|
zwp_linux_surface_synchronization_v1::Error::NoBuffer as u32,
|
||||||
"No buffer was attached.".into(),
|
"No buffer was attached.".into(),
|
||||||
),
|
),
|
||||||
|
@ -214,14 +182,12 @@ pub fn send_explicit_synchronization_error(attrs: &SurfaceAttributes, error: Exp
|
||||||
/// Initialize the explicit synchronization global
|
/// Initialize the explicit synchronization global
|
||||||
///
|
///
|
||||||
/// See module-level documentation for its use.
|
/// 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,
|
display: &mut Display,
|
||||||
compositor: CompositorToken<R>,
|
|
||||||
logger: L,
|
logger: L,
|
||||||
) -> Global<ZwpLinuxExplicitSynchronizationV1>
|
) -> Global<ZwpLinuxExplicitSynchronizationV1>
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
R: 'static,
|
|
||||||
{
|
{
|
||||||
let _log =
|
let _log =
|
||||||
crate::slog_or_fallback(logger).new(o!("smithay_module" => "wayland_explicit_synchronization"));
|
crate::slog_or_fallback(logger).new(o!("smithay_module" => "wayland_explicit_synchronization"));
|
||||||
|
@ -236,16 +202,17 @@ where
|
||||||
surface,
|
surface,
|
||||||
} = req
|
} = req
|
||||||
{
|
{
|
||||||
let exists = compositor.with_surface_data(&surface, |attrs| {
|
let exists = with_states(&surface, |states| {
|
||||||
attrs.user_data.insert_if_missing(|| ESUserData {
|
states.data_map.insert_if_missing(|| ESUserData {
|
||||||
state: RefCell::new(None),
|
state: RefCell::new(None),
|
||||||
});
|
});
|
||||||
attrs
|
states
|
||||||
.user_data
|
.data_map
|
||||||
.get::<ESUserData>()
|
.get::<ESUserData>()
|
||||||
.map(|ud| ud.state.borrow().is_some())
|
.map(|ud| ud.state.borrow().is_some())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
})
|
||||||
|
.unwrap_or(false);
|
||||||
if exists {
|
if exists {
|
||||||
explicit_sync.as_ref().post_error(
|
explicit_sync.as_ref().post_error(
|
||||||
zwp_linux_explicit_synchronization_v1::Error::SynchronizationExists as u32,
|
zwp_linux_explicit_synchronization_v1::Error::SynchronizationExists as u32,
|
||||||
|
@ -253,17 +220,12 @@ where
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let surface_sync = implement_surface_sync(id, surface.clone(), compositor);
|
let surface_sync = implement_surface_sync(id, surface.clone());
|
||||||
compositor.with_surface_data(&surface, |attrs| {
|
with_states(&surface, |states| {
|
||||||
let data = attrs.user_data.get::<ESUserData>().unwrap();
|
let data = states.data_map.get::<ESUserData>().unwrap();
|
||||||
*data.state.borrow_mut() = Some(InternalState {
|
*data.state.borrow_mut() = Some(surface_sync);
|
||||||
sync_state: ExplicitSyncState {
|
})
|
||||||
acquire: None,
|
.unwrap();
|
||||||
release: None,
|
|
||||||
},
|
|
||||||
sync_resource: surface_sync,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -271,14 +233,10 @@ where
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn implement_surface_sync<R>(
|
fn implement_surface_sync(
|
||||||
id: Main<ZwpLinuxSurfaceSynchronizationV1>,
|
id: Main<ZwpLinuxSurfaceSynchronizationV1>,
|
||||||
surface: WlSurface,
|
surface: WlSurface,
|
||||||
compositor: CompositorToken<R>,
|
) -> ZwpLinuxSurfaceSynchronizationV1 {
|
||||||
) -> ZwpLinuxSurfaceSynchronizationV1
|
|
||||||
where
|
|
||||||
R: 'static,
|
|
||||||
{
|
|
||||||
id.quick_assign(move |surface_sync, req, _| match req {
|
id.quick_assign(move |surface_sync, req, _| match req {
|
||||||
zwp_linux_surface_synchronization_v1::Request::SetAcquireFence { fd } => {
|
zwp_linux_surface_synchronization_v1::Request::SetAcquireFence { fd } => {
|
||||||
if !surface.as_ref().is_alive() {
|
if !surface.as_ref().is_alive() {
|
||||||
|
@ -287,19 +245,18 @@ where
|
||||||
"The associated wl_surface was destroyed.".into(),
|
"The associated wl_surface was destroyed.".into(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
compositor.with_surface_data(&surface, |attrs| {
|
with_states(&surface, |states| {
|
||||||
let data = attrs.user_data.get::<ESUserData>().unwrap();
|
let mut pending = states.cached_state.pending::<ExplicitSyncState>();
|
||||||
if let Some(state) = data.state.borrow_mut().deref_mut() {
|
if pending.acquire.is_some() {
|
||||||
if state.sync_state.acquire.is_some() {
|
|
||||||
surface_sync.as_ref().post_error(
|
surface_sync.as_ref().post_error(
|
||||||
zwp_linux_surface_synchronization_v1::Error::DuplicateFence as u32,
|
zwp_linux_surface_synchronization_v1::Error::DuplicateFence as u32,
|
||||||
"Multiple fences added for a single surface commit.".into(),
|
"Multiple fences added for a single surface commit.".into(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
state.sync_state.acquire = Some(fd);
|
pending.acquire = Some(fd);
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
});
|
.unwrap();
|
||||||
}
|
}
|
||||||
zwp_linux_surface_synchronization_v1::Request::GetRelease { release } => {
|
zwp_linux_surface_synchronization_v1::Request::GetRelease { release } => {
|
||||||
if !surface.as_ref().is_alive() {
|
if !surface.as_ref().is_alive() {
|
||||||
|
@ -308,30 +265,30 @@ where
|
||||||
"The associated wl_surface was destroyed.".into(),
|
"The associated wl_surface was destroyed.".into(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
compositor.with_surface_data(&surface, |attrs| {
|
with_states(&surface, |states| {
|
||||||
let data = attrs.user_data.get::<ESUserData>().unwrap();
|
let mut pending = states.cached_state.pending::<ExplicitSyncState>();
|
||||||
if let Some(state) = data.state.borrow_mut().deref_mut() {
|
if pending.release.is_some() {
|
||||||
if state.sync_state.acquire.is_some() {
|
|
||||||
surface_sync.as_ref().post_error(
|
surface_sync.as_ref().post_error(
|
||||||
zwp_linux_surface_synchronization_v1::Error::DuplicateRelease as u32,
|
zwp_linux_surface_synchronization_v1::Error::DuplicateRelease as u32,
|
||||||
"Multiple releases added for a single surface commit.".into(),
|
"Multiple releases added for a single surface commit.".into(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
release.quick_assign(|_, _, _| {});
|
release.quick_assign(|_, _, _| {});
|
||||||
state.sync_state.release = Some(ExplicitBufferRelease {
|
pending.release = Some(ExplicitBufferRelease {
|
||||||
release: release.deref().clone(),
|
release: release.deref().clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
});
|
.unwrap();
|
||||||
}
|
}
|
||||||
zwp_linux_surface_synchronization_v1::Request::Destroy => {
|
zwp_linux_surface_synchronization_v1::Request::Destroy => {
|
||||||
// disable the ESUserData
|
// disable the ESUserData
|
||||||
compositor.with_surface_data(&surface, |attrs| {
|
with_states(&surface, |states| {
|
||||||
if let Some(ref mut data) = attrs.user_data.get::<ESUserData>() {
|
if let Some(ref mut data) = states.data_map.get::<ESUserData>() {
|
||||||
*data.state.borrow_mut() = None;
|
*data.state.borrow_mut() = None;
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
});
|
});
|
||||||
|
|
|
@ -225,7 +225,7 @@ impl Output {
|
||||||
pub fn set_preferred(&self, mode: Mode) {
|
pub fn set_preferred(&self, mode: Mode) {
|
||||||
let mut inner = self.inner.lock().unwrap();
|
let mut inner = self.inner.lock().unwrap();
|
||||||
inner.preferred_mode = Some(mode);
|
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);
|
inner.modes.push(mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ impl Output {
|
||||||
/// Adds a mode to the list of known modes to this output
|
/// Adds a mode to the list of known modes to this output
|
||||||
pub fn add_mode(&self, mode: Mode) {
|
pub fn add_mode(&self, mode: Mode) {
|
||||||
let mut inner = self.inner.lock().unwrap();
|
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);
|
inner.modes.push(mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -270,7 +270,7 @@ impl Output {
|
||||||
) {
|
) {
|
||||||
let mut inner = self.inner.lock().unwrap();
|
let mut inner = self.inner.lock().unwrap();
|
||||||
if let Some(mode) = new_mode {
|
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.modes.push(mode);
|
||||||
}
|
}
|
||||||
inner.current_mode = new_mode;
|
inner.current_mode = new_mode;
|
||||||
|
|
|
@ -9,21 +9,13 @@
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # extern crate wayland_server;
|
//! # extern crate wayland_server;
|
||||||
//! # #[macro_use] extern crate smithay;
|
//! use smithay::wayland::seat::Seat;
|
||||||
//! 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]);
|
|
||||||
//!
|
//!
|
||||||
//! # let mut display = wayland_server::Display::new();
|
//! # let mut display = wayland_server::Display::new();
|
||||||
//! # let (compositor_token, _, _) = compositor_init::<Roles, _, _>(&mut display, |_, _, _, _| {}, None);
|
|
||||||
//! // insert the seat:
|
//! // insert the seat:
|
||||||
//! let (seat, seat_global) = Seat::new(
|
//! let (seat, seat_global) = Seat::new(
|
||||||
//! &mut display, // the display
|
//! &mut display, // the display
|
||||||
//! "seat-0".into(), // the name of the seat, will be advertized to clients
|
//! "seat-0".into(), // the name of the seat, will be advertized to clients
|
||||||
//! compositor_token.clone(), // the compositor token
|
|
||||||
//! None // insert a logger here
|
//! None // insert a logger here
|
||||||
//! );
|
//! );
|
||||||
//! ```
|
//! ```
|
||||||
|
@ -32,13 +24,15 @@
|
||||||
//!
|
//!
|
||||||
//! Once the seat is initialized, you can add capabilities to it.
|
//! Once the seat is initialized, you can add capabilities to it.
|
||||||
//!
|
//!
|
||||||
//! Currently, only pointer and keyboard capabilities are supported by
|
//! Currently, only pointer and keyboard capabilities are supported by smithay.
|
||||||
//! smithay.
|
|
||||||
//!
|
//!
|
||||||
//! You can add these capabilities via methods of the [`Seat`] struct:
|
//! 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
|
//! 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.
|
//! 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};
|
use std::{cell::RefCell, fmt, ops::Deref as _, rc::Rc};
|
||||||
|
|
||||||
|
@ -48,13 +42,11 @@ mod pointer;
|
||||||
pub use self::{
|
pub use self::{
|
||||||
keyboard::{keysyms, Error as KeyboardError, KeyboardHandle, Keysym, ModifiersState, XkbConfig},
|
keyboard::{keysyms, Error as KeyboardError, KeyboardHandle, Keysym, ModifiersState, XkbConfig},
|
||||||
pointer::{
|
pointer::{
|
||||||
AxisFrame, CursorImageRole, CursorImageStatus, GrabStartData, PointerGrab, PointerHandle,
|
AxisFrame, CursorImageAttributes, CursorImageStatus, GrabStartData, PointerGrab, PointerHandle,
|
||||||
PointerInnerHandle,
|
PointerInnerHandle,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::wayland::compositor::{roles::Role, CompositorToken};
|
|
||||||
|
|
||||||
use wayland_server::{
|
use wayland_server::{
|
||||||
protocol::{wl_seat, wl_surface},
|
protocol::{wl_seat, wl_surface},
|
||||||
Display, Filter, Global, Main, UserDataMap,
|
Display, Filter, Global, Main, UserDataMap,
|
||||||
|
@ -130,14 +122,8 @@ impl Seat {
|
||||||
/// You are provided with the state token to retrieve it (allowing
|
/// You are provided with the state token to retrieve it (allowing
|
||||||
/// you to add or remove capabilities from it), and the global handle,
|
/// you to add or remove capabilities from it), and the global handle,
|
||||||
/// in case you want to remove it.
|
/// in case you want to remove it.
|
||||||
pub fn new<R, L>(
|
pub fn new<L>(display: &mut Display, name: String, logger: L) -> (Seat, Global<wl_seat::WlSeat>)
|
||||||
display: &mut Display,
|
|
||||||
name: String,
|
|
||||||
token: CompositorToken<R>,
|
|
||||||
logger: L,
|
|
||||||
) -> (Seat, Global<wl_seat::WlSeat>)
|
|
||||||
where
|
where
|
||||||
R: Role<CursorImageRole> + 'static,
|
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
let log = crate::slog_or_fallback(logger);
|
let log = crate::slog_or_fallback(logger);
|
||||||
|
@ -155,7 +141,7 @@ impl Seat {
|
||||||
let global = display.create_global(
|
let global = display.create_global(
|
||||||
5,
|
5,
|
||||||
Filter::new(move |(new_seat, _version), _, _| {
|
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();
|
let mut inner = arc.inner.borrow_mut();
|
||||||
if seat.as_ref().version() >= 2 {
|
if seat.as_ref().version() >= 2 {
|
||||||
seat.name(arc.name.clone());
|
seat.name(arc.name.clone());
|
||||||
|
@ -197,32 +183,25 @@ impl Seat {
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate wayland_server;
|
/// # extern crate wayland_server;
|
||||||
/// # #[macro_use] extern crate smithay;
|
|
||||||
/// #
|
/// #
|
||||||
/// # use smithay::wayland::{seat::{Seat, CursorImageRole}, compositor::compositor_init};
|
/// # use smithay::wayland::seat::Seat;
|
||||||
/// #
|
|
||||||
/// # define_roles!(Roles => [CursorImage, CursorImageRole]);
|
|
||||||
/// #
|
/// #
|
||||||
/// # let mut display = wayland_server::Display::new();
|
/// # let mut display = wayland_server::Display::new();
|
||||||
/// # let (compositor_token, _, _) = compositor_init::<Roles, _, _>(&mut display, |_, _, _, _| {}, None);
|
|
||||||
/// # let (mut seat, seat_global) = Seat::new(
|
/// # let (mut seat, seat_global) = Seat::new(
|
||||||
/// # &mut display,
|
/// # &mut display,
|
||||||
/// # "seat-0".into(),
|
/// # "seat-0".into(),
|
||||||
/// # compositor_token.clone(),
|
|
||||||
/// # None
|
/// # None
|
||||||
/// # );
|
/// # );
|
||||||
/// let pointer_handle = seat.add_pointer(
|
/// let pointer_handle = seat.add_pointer(
|
||||||
/// compositor_token.clone(),
|
|
||||||
/// |new_status| { /* a closure handling requests from clients tot change the cursor icon */ }
|
/// |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
|
where
|
||||||
R: Role<CursorImageRole> + 'static,
|
|
||||||
F: FnMut(CursorImageStatus) + 'static,
|
F: FnMut(CursorImageStatus) + 'static,
|
||||||
{
|
{
|
||||||
let mut inner = self.arc.inner.borrow_mut();
|
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() {
|
if inner.pointer.is_some() {
|
||||||
// there is already a pointer, remove it and notify the clients
|
// there is already a pointer, remove it and notify the clients
|
||||||
// of the change
|
// of the change
|
||||||
|
@ -344,21 +323,14 @@ impl ::std::cmp::PartialEq for Seat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn implement_seat<R>(
|
fn implement_seat(seat: Main<wl_seat::WlSeat>, arc: Rc<SeatRc>) -> wl_seat::WlSeat {
|
||||||
seat: Main<wl_seat::WlSeat>,
|
|
||||||
arc: Rc<SeatRc>,
|
|
||||||
token: CompositorToken<R>,
|
|
||||||
) -> wl_seat::WlSeat
|
|
||||||
where
|
|
||||||
R: Role<CursorImageRole> + 'static,
|
|
||||||
{
|
|
||||||
let dest_arc = arc.clone();
|
let dest_arc = arc.clone();
|
||||||
seat.quick_assign(move |seat, request, _| {
|
seat.quick_assign(move |seat, request, _| {
|
||||||
let arc = seat.as_ref().user_data().get::<Rc<SeatRc>>().unwrap();
|
let arc = seat.as_ref().user_data().get::<Rc<SeatRc>>().unwrap();
|
||||||
let inner = arc.inner.borrow_mut();
|
let inner = arc.inner.borrow_mut();
|
||||||
match request {
|
match request {
|
||||||
wl_seat::Request::GetPointer { id } => {
|
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 {
|
if let Some(ref ptr_handle) = inner.pointer {
|
||||||
ptr_handle.new_pointer(pointer);
|
ptr_handle.new_pointer(pointer);
|
||||||
} else {
|
} 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::{
|
use wayland_server::{
|
||||||
protocol::{
|
protocol::{
|
||||||
|
@ -8,12 +8,14 @@ use wayland_server::{
|
||||||
Filter, Main,
|
Filter, Main,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::wayland::compositor::{roles::Role, CompositorToken};
|
use crate::wayland::compositor;
|
||||||
use crate::wayland::Serial;
|
use crate::wayland::Serial;
|
||||||
|
|
||||||
|
static CURSOR_IMAGE_ROLE: &str = "cursor_image";
|
||||||
|
|
||||||
/// The role representing a surface set as the pointer cursor
|
/// The role representing a surface set as the pointer cursor
|
||||||
#[derive(Debug, Default, Copy, Clone)]
|
#[derive(Debug, Default, Copy, Clone)]
|
||||||
pub struct CursorImageRole {
|
pub struct CursorImageAttributes {
|
||||||
/// Location of the hotspot of the pointer in the surface
|
/// Location of the hotspot of the pointer in the surface
|
||||||
pub hotspot: (i32, i32),
|
pub hotspot: (i32, i32),
|
||||||
}
|
}
|
||||||
|
@ -72,9 +74,8 @@ impl fmt::Debug for PointerInternal {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PointerInternal {
|
impl PointerInternal {
|
||||||
fn new<F, R>(token: CompositorToken<R>, mut cb: F) -> PointerInternal
|
fn new<F>(mut cb: F) -> PointerInternal
|
||||||
where
|
where
|
||||||
R: Role<CursorImageRole> + 'static,
|
|
||||||
F: FnMut(CursorImageStatus) + 'static,
|
F: FnMut(CursorImageStatus) + 'static,
|
||||||
{
|
{
|
||||||
let mut old_status = CursorImageStatus::Default;
|
let mut old_status = CursorImageStatus::Default;
|
||||||
|
@ -86,11 +87,7 @@ impl PointerInternal {
|
||||||
CursorImageStatus::Image(ref new_surface) if new_surface == &surface => {
|
CursorImageStatus::Image(ref new_surface) if new_surface == &surface => {
|
||||||
// don't remove the role, we are just re-binding the same 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)
|
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
|
where
|
||||||
R: Role<CursorImageRole> + 'static,
|
|
||||||
F: FnMut(CursorImageStatus) + 'static,
|
F: FnMut(CursorImageStatus) + 'static,
|
||||||
{
|
{
|
||||||
PointerHandle {
|
PointerHandle {
|
||||||
inner: Rc::new(RefCell::new(PointerInternal::new(token, cb))),
|
inner: Rc::new(RefCell::new(PointerInternal::new(cb))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn implement_pointer<R>(
|
pub(crate) fn implement_pointer(pointer: Main<WlPointer>, handle: Option<&PointerHandle>) -> WlPointer {
|
||||||
pointer: Main<WlPointer>,
|
|
||||||
handle: Option<&PointerHandle>,
|
|
||||||
token: CompositorToken<R>,
|
|
||||||
) -> WlPointer
|
|
||||||
where
|
|
||||||
R: Role<CursorImageRole> + 'static,
|
|
||||||
{
|
|
||||||
let inner = handle.map(|h| h.inner.clone());
|
let inner = handle.map(|h| h.inner.clone());
|
||||||
pointer.quick_assign(move |pointer, request, _data| {
|
pointer.quick_assign(move |pointer, request, _data| {
|
||||||
match request {
|
match request {
|
||||||
|
@ -606,14 +595,9 @@ where
|
||||||
if focus.as_ref().same_client_as(&pointer.as_ref()) {
|
if focus.as_ref().same_client_as(&pointer.as_ref()) {
|
||||||
match surface {
|
match surface {
|
||||||
Some(surface) => {
|
Some(surface) => {
|
||||||
let role_data = CursorImageRole {
|
// tolerate re-using the same surface
|
||||||
hotspot: (hotspot_x, hotspot_y),
|
if compositor::give_role(&surface, CURSOR_IMAGE_ROLE).is_err()
|
||||||
};
|
&& compositor::get_role(&surface) != Some(CURSOR_IMAGE_ROLE)
|
||||||
// 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()
|
|
||||||
{
|
{
|
||||||
pointer.as_ref().post_error(
|
pointer.as_ref().post_error(
|
||||||
wl_pointer::Error::Role as u32,
|
wl_pointer::Error::Role as u32,
|
||||||
|
@ -621,6 +605,20 @@ where
|
||||||
);
|
);
|
||||||
return;
|
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));
|
image_callback(CursorImageStatus::Image(surface));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
|
|
@ -23,37 +23,18 @@
|
||||||
//!
|
//!
|
||||||
//! ### Initialization
|
//! ### Initialization
|
||||||
//!
|
//!
|
||||||
//! To initialize this handler, simple use the [`wl_shell_init`]
|
//! To initialize this handler, simple use the [`wl_shell_init`] function provided in this module.
|
||||||
//! 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.
|
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! # extern crate wayland_server;
|
//! # extern crate wayland_server;
|
||||||
//! # #[macro_use] extern crate smithay;
|
|
||||||
//! # extern crate wayland_protocols;
|
|
||||||
//! #
|
//! #
|
||||||
//! use smithay::wayland::compositor::roles::*;
|
//! use smithay::wayland::shell::legacy::{wl_shell_init, ShellRequest};
|
||||||
//! 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]
|
|
||||||
//! );
|
|
||||||
//!
|
//!
|
||||||
//! # let mut display = wayland_server::Display::new();
|
//! # 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(
|
//! let (shell_state, _) = wl_shell_init(
|
||||||
//! &mut display,
|
//! &mut display,
|
||||||
//! // token from the compositor implementation
|
|
||||||
//! compositor_token,
|
|
||||||
//! // your implementation
|
//! // your implementation
|
||||||
//! |event: ShellRequest<_>| { /* ... */ },
|
//! |event: ShellRequest| { /* ... */ },
|
||||||
//! None // put a logger if you want
|
//! None // put a logger if you want
|
||||||
//! );
|
//! );
|
||||||
//!
|
//!
|
||||||
|
@ -66,8 +47,7 @@ use std::{
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::wayland::compositor::{roles::Role, CompositorToken};
|
use crate::wayland::{compositor, Serial};
|
||||||
use crate::wayland::Serial;
|
|
||||||
|
|
||||||
use wayland_server::{
|
use wayland_server::{
|
||||||
protocol::{wl_output, wl_seat, wl_shell, wl_shell_surface, wl_surface},
|
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
|
/// Metadata associated with the `wl_surface` role
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ShellSurfaceRole {
|
pub struct ShellSurfaceAttributes {
|
||||||
/// Title of the surface
|
/// Title of the surface
|
||||||
pub title: String,
|
pub title: String,
|
||||||
/// Class of the surface
|
/// Class of the surface
|
||||||
|
@ -89,28 +69,13 @@ pub struct ShellSurfaceRole {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A handle to a shell surface
|
/// A handle to a shell surface
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ShellSurface<R> {
|
pub struct ShellSurface {
|
||||||
wl_surface: wl_surface::WlSurface,
|
wl_surface: wl_surface::WlSurface,
|
||||||
shell_surface: wl_shell_surface::WlShellSurface,
|
shell_surface: wl_shell_surface::WlShellSurface,
|
||||||
token: CompositorToken<R>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We implement Clone manually because #[derive(..)] would require R: Clone.
|
impl ShellSurface {
|
||||||
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,
|
|
||||||
{
|
|
||||||
/// Is the shell surface referred by this handle still alive?
|
/// Is the shell surface referred by this handle still alive?
|
||||||
pub fn alive(&self) -> bool {
|
pub fn alive(&self) -> bool {
|
||||||
self.shell_surface.as_ref().is_alive() && self.wl_surface.as_ref().is_alive()
|
self.shell_surface.as_ref().is_alive() && self.wl_surface.as_ref().is_alive()
|
||||||
|
@ -145,8 +110,13 @@ where
|
||||||
if !self.alive() {
|
if !self.alive() {
|
||||||
return Err(PingError::DeadSurface);
|
return Err(PingError::DeadSurface);
|
||||||
}
|
}
|
||||||
self.token
|
compositor::with_states(&self.wl_surface, |states| {
|
||||||
.with_role_data(&self.wl_surface, |data| {
|
let mut data = states
|
||||||
|
.data_map
|
||||||
|
.get::<Mutex<ShellSurfaceAttributes>>()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap();
|
||||||
if let Some(pending_ping) = data.pending_ping {
|
if let Some(pending_ping) = data.pending_ping {
|
||||||
return Err(PingError::PingAlreadyPending(pending_ping));
|
return Err(PingError::PingAlreadyPending(pending_ping));
|
||||||
}
|
}
|
||||||
|
@ -225,13 +195,13 @@ pub enum ShellSurfaceKind {
|
||||||
|
|
||||||
/// A request triggered by a `wl_shell_surface`
|
/// A request triggered by a `wl_shell_surface`
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ShellRequest<R> {
|
pub enum ShellRequest {
|
||||||
/// A new shell surface was created
|
/// A new shell surface was created
|
||||||
///
|
///
|
||||||
/// by default it has no kind and this should not be displayed
|
/// by default it has no kind and this should not be displayed
|
||||||
NewShellSurface {
|
NewShellSurface {
|
||||||
/// The created surface
|
/// The created surface
|
||||||
surface: ShellSurface<R>,
|
surface: ShellSurface,
|
||||||
},
|
},
|
||||||
/// A pong event
|
/// A pong event
|
||||||
///
|
///
|
||||||
|
@ -239,14 +209,14 @@ pub enum ShellRequest<R> {
|
||||||
/// event, smithay has already checked that the responded serial was valid.
|
/// event, smithay has already checked that the responded serial was valid.
|
||||||
Pong {
|
Pong {
|
||||||
/// The surface that sent the pong
|
/// The surface that sent the pong
|
||||||
surface: ShellSurface<R>,
|
surface: ShellSurface,
|
||||||
},
|
},
|
||||||
/// Start of an interactive move
|
/// Start of an interactive move
|
||||||
///
|
///
|
||||||
/// The surface requests that an interactive move is started on it
|
/// The surface requests that an interactive move is started on it
|
||||||
Move {
|
Move {
|
||||||
/// The surface requesting the move
|
/// The surface requesting the move
|
||||||
surface: ShellSurface<R>,
|
surface: ShellSurface,
|
||||||
/// Serial of the implicit grab that initiated the move
|
/// Serial of the implicit grab that initiated the move
|
||||||
serial: Serial,
|
serial: Serial,
|
||||||
/// Seat associated with the move
|
/// Seat associated with the move
|
||||||
|
@ -257,7 +227,7 @@ pub enum ShellRequest<R> {
|
||||||
/// The surface requests that an interactive resize is started on it
|
/// The surface requests that an interactive resize is started on it
|
||||||
Resize {
|
Resize {
|
||||||
/// The surface requesting the resize
|
/// The surface requesting the resize
|
||||||
surface: ShellSurface<R>,
|
surface: ShellSurface,
|
||||||
/// Serial of the implicit grab that initiated the resize
|
/// Serial of the implicit grab that initiated the resize
|
||||||
serial: Serial,
|
serial: Serial,
|
||||||
/// Seat associated with the resize
|
/// Seat associated with the resize
|
||||||
|
@ -268,7 +238,7 @@ pub enum ShellRequest<R> {
|
||||||
/// The surface changed its kind
|
/// The surface changed its kind
|
||||||
SetKind {
|
SetKind {
|
||||||
/// The surface
|
/// The surface
|
||||||
surface: ShellSurface<R>,
|
surface: ShellSurface,
|
||||||
/// Its new kind
|
/// Its new kind
|
||||||
kind: ShellSurfaceKind,
|
kind: ShellSurfaceKind,
|
||||||
},
|
},
|
||||||
|
@ -279,36 +249,31 @@ pub enum ShellRequest<R> {
|
||||||
/// This state allows you to retrieve a list of surfaces
|
/// This state allows you to retrieve a list of surfaces
|
||||||
/// currently known to the shell global.
|
/// currently known to the shell global.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ShellState<R> {
|
pub struct ShellState {
|
||||||
known_surfaces: Vec<ShellSurface<R>>,
|
known_surfaces: Vec<ShellSurface>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> ShellState<R>
|
impl ShellState {
|
||||||
where
|
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
/// Cleans the internal surface storage by removing all dead surfaces
|
/// Cleans the internal surface storage by removing all dead surfaces
|
||||||
pub(crate) fn cleanup_surfaces(&mut self) {
|
pub(crate) fn cleanup_surfaces(&mut self) {
|
||||||
self.known_surfaces.retain(|s| s.alive());
|
self.known_surfaces.retain(|s| s.alive());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access all the shell surfaces known by this handler
|
/// Access all the shell surfaces known by this handler
|
||||||
pub fn surfaces(&self) -> &[ShellSurface<R>] {
|
pub fn surfaces(&self) -> &[ShellSurface] {
|
||||||
&self.known_surfaces[..]
|
&self.known_surfaces[..]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `wl_shell` global
|
/// Create a new `wl_shell` global
|
||||||
pub fn wl_shell_init<R, L, Impl>(
|
pub fn wl_shell_init<L, Impl>(
|
||||||
display: &mut Display,
|
display: &mut Display,
|
||||||
ctoken: CompositorToken<R>,
|
|
||||||
implementation: Impl,
|
implementation: Impl,
|
||||||
logger: L,
|
logger: L,
|
||||||
) -> (Arc<Mutex<ShellState<R>>>, Global<wl_shell::WlShell>)
|
) -> (Arc<Mutex<ShellState>>, Global<wl_shell::WlShell>)
|
||||||
where
|
where
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
Impl: FnMut(ShellRequest<R>) + 'static,
|
Impl: FnMut(ShellRequest) + 'static,
|
||||||
{
|
{
|
||||||
let _log = crate::slog_or_fallback(logger);
|
let _log = crate::slog_or_fallback(logger);
|
||||||
|
|
||||||
|
@ -322,7 +287,7 @@ where
|
||||||
let global = display.create_global(
|
let global = display.create_global(
|
||||||
1,
|
1,
|
||||||
Filter::new(move |(shell, _version), _, _data| {
|
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,
|
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 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>,
|
shell: Main<wl_shell::WlShell>,
|
||||||
ctoken: CompositorToken<R>,
|
|
||||||
implementation: Rc<RefCell<Impl>>,
|
implementation: Rc<RefCell<Impl>>,
|
||||||
state: Arc<Mutex<ShellState<R>>>,
|
state: Arc<Mutex<ShellState>>,
|
||||||
) where
|
) where
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
Impl: FnMut(ShellRequest) + 'static,
|
||||||
Impl: FnMut(ShellRequest<R>) + 'static,
|
|
||||||
{
|
{
|
||||||
shell.quick_assign(move |shell, req, _data| {
|
shell.quick_assign(move |shell, req, _data| {
|
||||||
let (id, surface) = match req {
|
let (id, surface) = match req {
|
||||||
wl_shell::Request::GetShellSurface { id, surface } => (id, surface),
|
wl_shell::Request::GetShellSurface { id, surface } => (id, surface),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let role_data = ShellSurfaceRole {
|
if compositor::give_role(&surface, WL_SHELL_SURFACE_ROLE).is_err() {
|
||||||
title: "".into(),
|
|
||||||
class: "".into(),
|
|
||||||
pending_ping: None,
|
|
||||||
};
|
|
||||||
if ctoken.give_role_with(&surface, role_data).is_err() {
|
|
||||||
shell
|
shell
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.post_error(wl_shell::Error::Role as u32, "Surface already has a role.".into());
|
.post_error(wl_shell::Error::Role as u32, "Surface already has a role.".into());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let shell_surface =
|
compositor::with_states(&surface, |states| {
|
||||||
implement_shell_surface(id, surface, implementation.clone(), ctoken, state.clone());
|
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
|
state
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.known_surfaces
|
.known_surfaces
|
||||||
.push(make_handle(&shell_surface, ctoken));
|
.push(make_handle(&shell_surface));
|
||||||
let mut imp = implementation.borrow_mut();
|
let mut imp = implementation.borrow_mut();
|
||||||
(&mut *imp)(ShellRequest::NewShellSurface {
|
(&mut *imp)(ShellRequest::NewShellSurface {
|
||||||
surface: make_handle(&shell_surface, ctoken),
|
surface: make_handle(&shell_surface),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_handle<R>(
|
fn make_handle(shell_surface: &wl_shell_surface::WlShellSurface) -> ShellSurface {
|
||||||
shell_surface: &wl_shell_surface::WlShellSurface,
|
|
||||||
token: CompositorToken<R>,
|
|
||||||
) -> ShellSurface<R>
|
|
||||||
where
|
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let data = shell_surface
|
let data = shell_surface
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
ShellSurface {
|
ShellSurface {
|
||||||
wl_surface: data.surface.clone(),
|
wl_surface: data.surface.clone(),
|
||||||
shell_surface: shell_surface.clone(),
|
shell_surface: shell_surface.clone(),
|
||||||
token,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct ShellSurfaceUserData<R> {
|
pub(crate) struct ShellSurfaceUserData {
|
||||||
surface: wl_surface::WlSurface,
|
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>,
|
shell_surface: Main<wl_shell_surface::WlShellSurface>,
|
||||||
surface: wl_surface::WlSurface,
|
surface: wl_surface::WlSurface,
|
||||||
implementation: Rc<RefCell<Impl>>,
|
implementation: Rc<RefCell<Impl>>,
|
||||||
ctoken: CompositorToken<R>,
|
state: Arc<Mutex<ShellState>>,
|
||||||
state: Arc<Mutex<ShellState<R>>>,
|
|
||||||
) -> wl_shell_surface::WlShellSurface
|
) -> wl_shell_surface::WlShellSurface
|
||||||
where
|
where
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
Impl: FnMut(ShellRequest) + 'static,
|
||||||
Impl: FnMut(ShellRequest<R>) + 'static,
|
|
||||||
{
|
{
|
||||||
use self::wl_shell_surface::Request;
|
use self::wl_shell_surface::Request;
|
||||||
shell_surface.quick_assign(move |shell_surface, req, _data| {
|
shell_surface.quick_assign(move |shell_surface, req, _data| {
|
||||||
let data = shell_surface
|
let data = shell_surface
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut user_impl = implementation.borrow_mut();
|
let mut user_impl = implementation.borrow_mut();
|
||||||
match req {
|
match req {
|
||||||
Request::Pong { serial } => {
|
Request::Pong { serial } => {
|
||||||
let serial = Serial::from(serial);
|
let serial = Serial::from(serial);
|
||||||
let valid = ctoken
|
let valid = compositor::with_states(&data.surface, |states| {
|
||||||
.with_role_data(&data.surface, |data| {
|
let mut guard = states
|
||||||
if data.pending_ping == Some(serial) {
|
.data_map
|
||||||
data.pending_ping = None;
|
.get::<Mutex<ShellSurfaceAttributes>>()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap();
|
||||||
|
if guard.pending_ping == Some(serial) {
|
||||||
|
guard.pending_ping = None;
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.expect("wl_shell_surface exists but surface has not the right role?");
|
.unwrap();
|
||||||
if valid {
|
if valid {
|
||||||
(&mut *user_impl)(ShellRequest::Pong {
|
(&mut *user_impl)(ShellRequest::Pong {
|
||||||
surface: make_handle(&shell_surface, ctoken),
|
surface: make_handle(&shell_surface),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Request::Move { seat, serial } => {
|
Request::Move { seat, serial } => {
|
||||||
let serial = Serial::from(serial);
|
let serial = Serial::from(serial);
|
||||||
(&mut *user_impl)(ShellRequest::Move {
|
(&mut *user_impl)(ShellRequest::Move {
|
||||||
surface: make_handle(&shell_surface, ctoken),
|
surface: make_handle(&shell_surface),
|
||||||
serial,
|
serial,
|
||||||
seat,
|
seat,
|
||||||
})
|
})
|
||||||
|
@ -127,18 +127,18 @@ where
|
||||||
Request::Resize { seat, serial, edges } => {
|
Request::Resize { seat, serial, edges } => {
|
||||||
let serial = Serial::from(serial);
|
let serial = Serial::from(serial);
|
||||||
(&mut *user_impl)(ShellRequest::Resize {
|
(&mut *user_impl)(ShellRequest::Resize {
|
||||||
surface: make_handle(&shell_surface, ctoken),
|
surface: make_handle(&shell_surface),
|
||||||
serial,
|
serial,
|
||||||
seat,
|
seat,
|
||||||
edges,
|
edges,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Request::SetToplevel => (&mut *user_impl)(ShellRequest::SetKind {
|
Request::SetToplevel => (&mut *user_impl)(ShellRequest::SetKind {
|
||||||
surface: make_handle(&shell_surface, ctoken),
|
surface: make_handle(&shell_surface),
|
||||||
kind: ShellSurfaceKind::Toplevel,
|
kind: ShellSurfaceKind::Toplevel,
|
||||||
}),
|
}),
|
||||||
Request::SetTransient { parent, x, y, flags } => (&mut *user_impl)(ShellRequest::SetKind {
|
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 {
|
kind: ShellSurfaceKind::Transient {
|
||||||
parent,
|
parent,
|
||||||
location: (x, y),
|
location: (x, y),
|
||||||
|
@ -150,7 +150,7 @@ where
|
||||||
framerate,
|
framerate,
|
||||||
output,
|
output,
|
||||||
} => (&mut *user_impl)(ShellRequest::SetKind {
|
} => (&mut *user_impl)(ShellRequest::SetKind {
|
||||||
surface: make_handle(&shell_surface, ctoken),
|
surface: make_handle(&shell_surface),
|
||||||
kind: ShellSurfaceKind::Fullscreen {
|
kind: ShellSurfaceKind::Fullscreen {
|
||||||
method,
|
method,
|
||||||
framerate,
|
framerate,
|
||||||
|
@ -167,7 +167,7 @@ where
|
||||||
} => {
|
} => {
|
||||||
let serial = Serial::from(serial);
|
let serial = Serial::from(serial);
|
||||||
(&mut *user_impl)(ShellRequest::SetKind {
|
(&mut *user_impl)(ShellRequest::SetKind {
|
||||||
surface: make_handle(&shell_surface, ctoken),
|
surface: make_handle(&shell_surface),
|
||||||
kind: ShellSurfaceKind::Popup {
|
kind: ShellSurfaceKind::Popup {
|
||||||
parent,
|
parent,
|
||||||
serial,
|
serial,
|
||||||
|
@ -178,18 +178,32 @@ where
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Request::SetMaximized { output } => (&mut *user_impl)(ShellRequest::SetKind {
|
Request::SetMaximized { output } => (&mut *user_impl)(ShellRequest::SetKind {
|
||||||
surface: make_handle(&shell_surface, ctoken),
|
surface: make_handle(&shell_surface),
|
||||||
kind: ShellSurfaceKind::Maximized { output },
|
kind: ShellSurfaceKind::Maximized { output },
|
||||||
}),
|
}),
|
||||||
Request::SetTitle { title } => {
|
Request::SetTitle { title } => {
|
||||||
ctoken
|
compositor::with_states(&data.surface, |states| {
|
||||||
.with_role_data(&data.surface, |data| data.title = title)
|
let mut guard = states
|
||||||
.expect("wl_shell_surface exists but surface has not shell_surface role?!");
|
.data_map
|
||||||
|
.get::<Mutex<ShellSurfaceAttributes>>()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap();
|
||||||
|
guard.title = title;
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
Request::SetClass { class_ } => {
|
Request::SetClass { class_ } => {
|
||||||
ctoken
|
compositor::with_states(&data.surface, |states| {
|
||||||
.with_role_data(&data.surface, |data| data.class = class_)
|
let mut guard = states
|
||||||
.expect("wl_shell_surface exists but surface has not shell_surface role?!");
|
.data_map
|
||||||
|
.get::<Mutex<ShellSurfaceAttributes>>()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap();
|
||||||
|
guard.class = class_;
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
@ -200,7 +214,7 @@ where
|
||||||
let data = shell_surface
|
let data = shell_surface
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
data.state.lock().unwrap().cleanup_surfaces();
|
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 std::{cell::RefCell, ops::Deref as _, sync::Mutex};
|
||||||
|
|
||||||
use crate::wayland::compositor::{roles::*, CompositorToken};
|
use crate::wayland::compositor;
|
||||||
use crate::wayland::shell::xdg::{ConfigureError, PopupState};
|
use crate::wayland::shell::xdg::PopupState;
|
||||||
use crate::wayland::Serial;
|
use crate::wayland::Serial;
|
||||||
use wayland_protocols::xdg_shell::server::{
|
use wayland_protocols::xdg_shell::server::{
|
||||||
xdg_popup, xdg_positioner, xdg_surface, xdg_toplevel, xdg_wm_base,
|
xdg_popup, xdg_positioner, xdg_surface, xdg_toplevel, xdg_wm_base,
|
||||||
|
@ -12,25 +13,25 @@ use crate::utils::Rectangle;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
make_shell_client_data, PopupConfigure, PopupKind, PositionerState, ShellClient, ShellClientData,
|
make_shell_client_data, PopupConfigure, PopupKind, PositionerState, ShellClient, ShellClientData,
|
||||||
ShellData, ToplevelClientPending, ToplevelConfigure, ToplevelKind, XdgPopupSurfaceRole,
|
ShellData, SurfaceCachedState, ToplevelConfigure, ToplevelKind, XdgPopupSurfaceRoleAttributes,
|
||||||
XdgPopupSurfaceRoleAttributes, XdgRequest, XdgToplevelSurfaceRole, XdgToplevelSurfaceRoleAttributes,
|
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: Main<xdg_wm_base::XdgWmBase>,
|
||||||
shell_data: &ShellData<R>,
|
shell_data: &ShellData,
|
||||||
) -> xdg_wm_base::XdgWmBase
|
) -> xdg_wm_base::XdgWmBase {
|
||||||
where
|
shell.quick_assign(|shell, req, _data| wm_implementation(req, shell.deref().clone()));
|
||||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
shell.quick_assign(|shell, req, _data| wm_implementation::<R>(req, shell.deref().clone()));
|
|
||||||
shell.as_ref().user_data().set(|| ShellUserData {
|
shell.as_ref().user_data().set(|| ShellUserData {
|
||||||
shell_data: shell_data.clone(),
|
shell_data: shell_data.clone(),
|
||||||
client_data: Mutex::new(make_shell_client_data()),
|
client_data: Mutex::new(make_shell_client_data()),
|
||||||
});
|
});
|
||||||
let mut user_impl = shell_data.user_impl.borrow_mut();
|
let mut user_impl = shell_data.user_impl.borrow_mut();
|
||||||
(&mut *user_impl)(XdgRequest::NewClient {
|
(&mut *user_impl)(XdgRequest::NewClient {
|
||||||
client: make_shell_client(&shell, shell_data.compositor_token),
|
client: make_shell_client(&shell),
|
||||||
});
|
});
|
||||||
shell.deref().clone()
|
shell.deref().clone()
|
||||||
}
|
}
|
||||||
|
@ -39,26 +40,19 @@ where
|
||||||
* xdg_shell
|
* xdg_shell
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub(crate) struct ShellUserData<R> {
|
pub(crate) struct ShellUserData {
|
||||||
shell_data: ShellData<R>,
|
shell_data: ShellData,
|
||||||
pub(crate) client_data: Mutex<ShellClientData>,
|
pub(crate) client_data: Mutex<ShellClientData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn make_shell_client<R>(
|
pub(crate) fn make_shell_client(resource: &xdg_wm_base::XdgWmBase) -> ShellClient {
|
||||||
resource: &xdg_wm_base::XdgWmBase,
|
|
||||||
token: CompositorToken<R>,
|
|
||||||
) -> ShellClient<R> {
|
|
||||||
ShellClient {
|
ShellClient {
|
||||||
kind: super::ShellClientKind::Xdg(resource.clone()),
|
kind: super::ShellClientKind::Xdg(resource.clone()),
|
||||||
_token: token,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wm_implementation<R>(request: xdg_wm_base::Request, shell: xdg_wm_base::XdgWmBase)
|
fn wm_implementation(request: xdg_wm_base::Request, shell: xdg_wm_base::XdgWmBase) {
|
||||||
where
|
let data = shell.as_ref().user_data().get::<ShellUserData>().unwrap();
|
||||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let data = shell.as_ref().user_data().get::<ShellUserData<R>>().unwrap();
|
|
||||||
match request {
|
match request {
|
||||||
xdg_wm_base::Request::Destroy => {
|
xdg_wm_base::Request::Destroy => {
|
||||||
// all is handled by destructor
|
// all is handled by destructor
|
||||||
|
@ -70,14 +64,13 @@ where
|
||||||
// Do not assign a role to the surface here
|
// Do not assign a role to the surface here
|
||||||
// xdg_surface is not role, only xdg_toplevel and
|
// xdg_surface is not role, only xdg_toplevel and
|
||||||
// xdg_popup are defined as roles
|
// xdg_popup are defined as roles
|
||||||
id.quick_assign(|surface, req, _data| {
|
id.quick_assign(|surface, req, _data| xdg_surface_implementation(req, surface.deref().clone()));
|
||||||
xdg_surface_implementation::<R>(req, surface.deref().clone())
|
id.assign_destructor(Filter::new(|surface, _, _data| destroy_surface(surface)));
|
||||||
});
|
|
||||||
id.assign_destructor(Filter::new(|surface, _, _data| destroy_surface::<R>(surface)));
|
|
||||||
id.as_ref().user_data().set(|| XdgSurfaceUserData {
|
id.as_ref().user_data().set(|| XdgSurfaceUserData {
|
||||||
shell_data: data.shell_data.clone(),
|
shell_data: data.shell_data.clone(),
|
||||||
wl_surface: surface,
|
wl_surface: surface,
|
||||||
wm_base: shell.clone(),
|
wm_base: shell.clone(),
|
||||||
|
has_active_role: AtomicBool::new(false),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
xdg_wm_base::Request::Pong { serial } => {
|
xdg_wm_base::Request::Pong { serial } => {
|
||||||
|
@ -94,7 +87,7 @@ where
|
||||||
if valid {
|
if valid {
|
||||||
let mut user_impl = data.shell_data.user_impl.borrow_mut();
|
let mut user_impl = data.shell_data.user_impl.borrow_mut();
|
||||||
(&mut *user_impl)(XdgRequest::ClientPong {
|
(&mut *user_impl)(XdgRequest::ClientPong {
|
||||||
client: make_shell_client(&shell, 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
|
* xdg_surface
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct XdgSurfaceUserData<R> {
|
struct XdgSurfaceUserData {
|
||||||
shell_data: ShellData<R>,
|
shell_data: ShellData,
|
||||||
wl_surface: wl_surface::WlSurface,
|
wl_surface: wl_surface::WlSurface,
|
||||||
wm_base: xdg_wm_base::XdgWmBase,
|
wm_base: xdg_wm_base::XdgWmBase,
|
||||||
|
has_active_role: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn destroy_surface<R>(surface: xdg_surface::XdgSurface)
|
fn destroy_surface(surface: xdg_surface::XdgSurface) {
|
||||||
where
|
let data = surface.as_ref().user_data().get::<XdgSurfaceUserData>().unwrap();
|
||||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let data = surface
|
|
||||||
.as_ref()
|
|
||||||
.user_data()
|
|
||||||
.get::<XdgSurfaceUserData<R>>()
|
|
||||||
.unwrap();
|
|
||||||
if !data.wl_surface.as_ref().is_alive() {
|
if !data.wl_surface.as_ref().is_alive() {
|
||||||
// the wl_surface is destroyed, this means the client is not
|
// the wl_surface is destroyed, this means the client is not
|
||||||
// trying to change the role but it's a cleanup (possibly a
|
// trying to change the role but it's a cleanup (possibly a
|
||||||
|
@ -191,25 +178,12 @@ where
|
||||||
return;
|
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.
|
// No role assigned to the surface, we can exit early.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let has_active_xdg_role = data
|
if data.has_active_role.load(Ordering::Acquire) {
|
||||||
.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 {
|
|
||||||
data.wm_base.as_ref().post_error(
|
data.wm_base.as_ref().post_error(
|
||||||
xdg_wm_base::Error::Role as u32,
|
xdg_wm_base::Error::Role as u32,
|
||||||
"xdg_surface was destroyed before its role object".into(),
|
"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)
|
fn xdg_surface_implementation(request: xdg_surface::Request, xdg_surface: xdg_surface::XdgSurface) {
|
||||||
where
|
|
||||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let data = xdg_surface
|
let data = xdg_surface
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<XdgSurfaceUserData<R>>()
|
.get::<XdgSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match request {
|
match request {
|
||||||
xdg_surface::Request::Destroy => {
|
xdg_surface::Request::Destroy => {
|
||||||
|
@ -235,14 +206,7 @@ where
|
||||||
let surface = &data.wl_surface;
|
let surface = &data.wl_surface;
|
||||||
let shell = &data.wm_base;
|
let shell = &data.wm_base;
|
||||||
|
|
||||||
let role_data = XdgToplevelSurfaceRole::Some(Default::default());
|
if compositor::give_role(surface, XDG_TOPLEVEL_ROLE).is_err() {
|
||||||
|
|
||||||
if data
|
|
||||||
.shell_data
|
|
||||||
.compositor_token
|
|
||||||
.give_role_with(&surface, role_data)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
shell.as_ref().post_error(
|
shell.as_ref().post_error(
|
||||||
xdg_wm_base::Error::Role as u32,
|
xdg_wm_base::Error::Role as u32,
|
||||||
"Surface already has a role.".into(),
|
"Surface already has a role.".into(),
|
||||||
|
@ -250,10 +214,19 @@ where
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
id.quick_assign(|toplevel, req, _data| {
|
data.has_active_role.store(true, Ordering::Release);
|
||||||
toplevel_implementation::<R>(req, toplevel.deref().clone())
|
|
||||||
});
|
compositor::with_states(surface, |states| {
|
||||||
id.assign_destructor(Filter::new(|toplevel, _, _data| destroy_toplevel::<R>(toplevel)));
|
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 {
|
id.as_ref().user_data().set(|| ShellSurfaceUserData {
|
||||||
shell_data: data.shell_data.clone(),
|
shell_data: data.shell_data.clone(),
|
||||||
wl_surface: data.wl_surface.clone(),
|
wl_surface: data.wl_surface.clone(),
|
||||||
|
@ -286,11 +259,7 @@ where
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
let parent_surface = parent.map(|parent| {
|
let parent_surface = parent.map(|parent| {
|
||||||
let parent_data = parent
|
let parent_data = parent.as_ref().user_data().get::<XdgSurfaceUserData>().unwrap();
|
||||||
.as_ref()
|
|
||||||
.user_data()
|
|
||||||
.get::<XdgSurfaceUserData<R>>()
|
|
||||||
.unwrap();
|
|
||||||
parent_data.wl_surface.clone()
|
parent_data.wl_surface.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -298,21 +267,15 @@ where
|
||||||
let surface = &data.wl_surface;
|
let surface = &data.wl_surface;
|
||||||
let shell = &data.wm_base;
|
let shell = &data.wm_base;
|
||||||
|
|
||||||
let role_data = XdgPopupSurfaceRole::Some(XdgPopupSurfaceRoleAttributes {
|
let attributes = XdgPopupSurfaceRoleAttributes {
|
||||||
parent: parent_surface,
|
parent: parent_surface,
|
||||||
server_pending: Some(PopupState {
|
server_pending: Some(PopupState {
|
||||||
// Set the positioner data as the popup geometry
|
// Set the positioner data as the popup geometry
|
||||||
geometry: positioner_data.get_geometry(),
|
geometry: positioner_data.get_geometry(),
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
};
|
||||||
|
if compositor::give_role(surface, XDG_POPUP_ROLE).is_err() {
|
||||||
if data
|
|
||||||
.shell_data
|
|
||||||
.compositor_token
|
|
||||||
.give_role_with(&surface, role_data)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
shell.as_ref().post_error(
|
shell.as_ref().post_error(
|
||||||
xdg_wm_base::Error::Role as u32,
|
xdg_wm_base::Error::Role as u32,
|
||||||
"Surface already has a role.".into(),
|
"Surface already has a role.".into(),
|
||||||
|
@ -320,8 +283,25 @@ where
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
id.quick_assign(|popup, req, _data| xdg_popup_implementation::<R>(req, popup.deref().clone()));
|
data.has_active_role.store(true, Ordering::Release);
|
||||||
id.assign_destructor(Filter::new(|popup, _, _data| destroy_popup::<R>(popup)));
|
|
||||||
|
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 {
|
id.as_ref().user_data().set(|| ShellSurfaceUserData {
|
||||||
shell_data: data.shell_data.clone(),
|
shell_data: data.shell_data.clone(),
|
||||||
wl_surface: data.wl_surface.clone(),
|
wl_surface: data.wl_surface.clone(),
|
||||||
|
@ -346,33 +326,28 @@ where
|
||||||
// which is a protocol error.
|
// which is a protocol error.
|
||||||
let surface = &data.wl_surface;
|
let surface = &data.wl_surface;
|
||||||
|
|
||||||
if !data.shell_data.compositor_token.has_a_role(surface) {
|
let role = compositor::get_role(surface);
|
||||||
data.wm_base.as_ref().post_error(
|
|
||||||
|
if role.is_none() {
|
||||||
|
xdg_surface.as_ref().post_error(
|
||||||
xdg_surface::Error::NotConstructed as u32,
|
xdg_surface::Error::NotConstructed as u32,
|
||||||
"xdg_surface must have a role.".into(),
|
"xdg_surface must have a role.".into(),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the next window geometry here, the geometry will be moved from
|
if role != Some(XDG_TOPLEVEL_ROLE) && role != Some(XDG_POPUP_ROLE) {
|
||||||
// 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 {
|
|
||||||
data.wm_base.as_ref().post_error(
|
data.wm_base.as_ref().post_error(
|
||||||
xdg_wm_base::Error::Role as u32,
|
xdg_wm_base::Error::Role as u32,
|
||||||
"xdg_surface must have a role of xdg_toplevel or xdg_popup.".into(),
|
"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 } => {
|
xdg_surface::Request::AckConfigure { serial } => {
|
||||||
let serial = Serial::from(serial);
|
let serial = Serial::from(serial);
|
||||||
|
@ -381,8 +356,8 @@ where
|
||||||
// Check the role of the surface, this can be either xdg_toplevel
|
// 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
|
// or xdg_popup. If none of the role matches the xdg_surface has no role set
|
||||||
// which is a protocol error.
|
// which is a protocol error.
|
||||||
if !data.shell_data.compositor_token.has_a_role(surface) {
|
if compositor::get_role(surface).is_none() {
|
||||||
data.wm_base.as_ref().post_error(
|
xdg_surface.as_ref().post_error(
|
||||||
xdg_surface::Error::NotConstructed as u32,
|
xdg_surface::Error::NotConstructed as u32,
|
||||||
"xdg_surface must have a role.".into(),
|
"xdg_surface must have a role.".into(),
|
||||||
);
|
);
|
||||||
|
@ -401,21 +376,38 @@ where
|
||||||
// width, height, min/max size, maximized, fullscreen, resizing, activated
|
// width, height, min/max size, maximized, fullscreen, resizing, activated
|
||||||
//
|
//
|
||||||
// This can be used to integrate custom protocol extensions
|
// This can be used to integrate custom protocol extensions
|
||||||
//
|
let found_configure = compositor::with_states(surface, |states| {
|
||||||
let configure = match data
|
if states.role == Some(XDG_TOPLEVEL_ROLE) {
|
||||||
.shell_data
|
Ok(states
|
||||||
.compositor_token
|
.data_map
|
||||||
.with_xdg_role(surface, |role| role.ack_configure(serial))
|
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
|
||||||
{
|
.unwrap()
|
||||||
Ok(Ok(configure)) => configure,
|
.lock()
|
||||||
Ok(Err(ConfigureError::SerialNotFound(serial))) => {
|
.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(
|
data.wm_base.as_ref().post_error(
|
||||||
xdg_wm_base::Error::InvalidSurfaceState as u32,
|
xdg_wm_base::Error::InvalidSurfaceState as u32,
|
||||||
format!("wrong configure serial: {}", <u32>::from(serial)),
|
format!("wrong configure serial: {}", <u32>::from(serial)),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(()) => {
|
||||||
data.wm_base.as_ref().post_error(
|
data.wm_base.as_ref().post_error(
|
||||||
xdg_wm_base::Error::Role as u32,
|
xdg_wm_base::Error::Role as u32,
|
||||||
"xdg_surface must have a role of xdg_toplevel or xdg_popup.".into(),
|
"xdg_surface must have a role of xdg_toplevel or xdg_popup.".into(),
|
||||||
|
@ -438,64 +430,54 @@ where
|
||||||
* xdg_toplevel
|
* xdg_toplevel
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub(crate) struct ShellSurfaceUserData<R> {
|
pub(crate) struct ShellSurfaceUserData {
|
||||||
pub(crate) shell_data: ShellData<R>,
|
pub(crate) shell_data: ShellData,
|
||||||
pub(crate) wl_surface: wl_surface::WlSurface,
|
pub(crate) wl_surface: wl_surface::WlSurface,
|
||||||
pub(crate) wm_base: xdg_wm_base::XdgWmBase,
|
pub(crate) wm_base: xdg_wm_base::XdgWmBase,
|
||||||
pub(crate) xdg_surface: xdg_surface::XdgSurface,
|
pub(crate) xdg_surface: xdg_surface::XdgSurface,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility functions allowing to factor out a lot of the upcoming logic
|
// Utility functions allowing to factor out a lot of the upcoming logic
|
||||||
fn with_surface_toplevel_role_data<R, F, T>(
|
fn with_surface_toplevel_role_data<F, T>(toplevel: &xdg_toplevel::XdgToplevel, f: F) -> T
|
||||||
shell_data: &ShellData<R>,
|
|
||||||
toplevel: &xdg_toplevel::XdgToplevel,
|
|
||||||
f: F,
|
|
||||||
) -> T
|
|
||||||
where
|
where
|
||||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
|
||||||
F: FnOnce(&mut XdgToplevelSurfaceRoleAttributes) -> T,
|
F: FnOnce(&mut XdgToplevelSurfaceRoleAttributes) -> T,
|
||||||
{
|
{
|
||||||
let data = toplevel
|
let data = toplevel
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
shell_data
|
compositor::with_states(&data.wl_surface, |states| {
|
||||||
.compositor_token
|
f(&mut *states
|
||||||
.with_role_data::<XdgToplevelSurfaceRole, _, _>(&data.wl_surface, |role| {
|
.data_map
|
||||||
let attributes = role
|
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
|
||||||
.as_mut()
|
.unwrap()
|
||||||
.expect("xdg_toplevel exists but role has been destroyed?!");
|
.lock()
|
||||||
f(attributes)
|
.unwrap())
|
||||||
})
|
})
|
||||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!")
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_surface_toplevel_client_pending<R, F, T>(
|
fn with_toplevel_pending_state<F, T>(toplevel: &xdg_toplevel::XdgToplevel, f: F) -> T
|
||||||
shell_data: &ShellData<R>,
|
|
||||||
toplevel: &xdg_toplevel::XdgToplevel,
|
|
||||||
f: F,
|
|
||||||
) -> T
|
|
||||||
where
|
where
|
||||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
F: FnOnce(&mut SurfaceCachedState) -> T,
|
||||||
F: FnOnce(&mut ToplevelClientPending) -> T,
|
|
||||||
{
|
{
|
||||||
with_surface_toplevel_role_data(shell_data, toplevel, |data| {
|
let data = toplevel
|
||||||
if data.client_pending.is_none() {
|
.as_ref()
|
||||||
data.client_pending = Some(Default::default());
|
.user_data()
|
||||||
}
|
.get::<ShellSurfaceUserData>()
|
||||||
f(&mut data.client_pending.as_mut().unwrap())
|
.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)
|
pub fn send_toplevel_configure(resource: &xdg_toplevel::XdgToplevel, configure: ToplevelConfigure) {
|
||||||
where
|
|
||||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let data = resource
|
let data = resource
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (width, height) = configure.state.size.unwrap_or((0, 0));
|
let (width, height) = configure.state.size.unwrap_or((0, 0));
|
||||||
|
@ -518,27 +500,23 @@ where
|
||||||
data.xdg_surface.configure(serial.into());
|
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
|
let data = resource
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
super::ToplevelSurface {
|
super::ToplevelSurface {
|
||||||
wl_surface: data.wl_surface.clone(),
|
wl_surface: data.wl_surface.clone(),
|
||||||
shell_surface: ToplevelKind::Xdg(resource.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)
|
fn toplevel_implementation(request: xdg_toplevel::Request, toplevel: xdg_toplevel::XdgToplevel) {
|
||||||
where
|
|
||||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let data = toplevel
|
let data = toplevel
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match request {
|
match request {
|
||||||
xdg_toplevel::Request::Destroy => {
|
xdg_toplevel::Request::Destroy => {
|
||||||
|
@ -546,12 +524,12 @@ where
|
||||||
}
|
}
|
||||||
xdg_toplevel::Request::SetParent { parent } => {
|
xdg_toplevel::Request::SetParent { parent } => {
|
||||||
// Parent is not double buffered, we can set it directly
|
// 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| {
|
data.parent = parent.map(|toplevel_surface_parent| {
|
||||||
toplevel_surface_parent
|
toplevel_surface_parent
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.wl_surface
|
.wl_surface
|
||||||
.clone()
|
.clone()
|
||||||
|
@ -560,13 +538,13 @@ where
|
||||||
}
|
}
|
||||||
xdg_toplevel::Request::SetTitle { title } => {
|
xdg_toplevel::Request::SetTitle { title } => {
|
||||||
// Title is not double buffered, we can set it directly
|
// 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);
|
data.title = Some(title);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
xdg_toplevel::Request::SetAppId { app_id } => {
|
xdg_toplevel::Request::SetAppId { app_id } => {
|
||||||
// AppId is not double buffered, we can set it directly
|
// 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);
|
role.app_id = Some(app_id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -606,13 +584,13 @@ where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
xdg_toplevel::Request::SetMaxSize { width, height } => {
|
xdg_toplevel::Request::SetMaxSize { width, height } => {
|
||||||
with_surface_toplevel_client_pending(&data.shell_data, &toplevel, |toplevel_data| {
|
with_toplevel_pending_state(&toplevel, |toplevel_data| {
|
||||||
toplevel_data.max_size = Some((width, height));
|
toplevel_data.max_size = (width, height);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
xdg_toplevel::Request::SetMinSize { width, height } => {
|
xdg_toplevel::Request::SetMinSize { width, height } => {
|
||||||
with_surface_toplevel_client_pending(&data.shell_data, &toplevel, |toplevel_data| {
|
with_toplevel_pending_state(&toplevel, |toplevel_data| {
|
||||||
toplevel_data.min_size = Some((width, height));
|
toplevel_data.min_size = (width, height);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
xdg_toplevel::Request::SetMaximized => {
|
xdg_toplevel::Request::SetMaximized => {
|
||||||
|
@ -649,26 +627,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn destroy_toplevel<R>(toplevel: xdg_toplevel::XdgToplevel)
|
fn destroy_toplevel(toplevel: xdg_toplevel::XdgToplevel) {
|
||||||
where
|
|
||||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let data = toplevel
|
let data = toplevel
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if !data.wl_surface.as_ref().is_alive() {
|
if let Some(data) = data.xdg_surface.as_ref().user_data().get::<XdgSurfaceUserData>() {
|
||||||
// the wl_surface is destroyed, this means the client is not
|
data.has_active_role.store(false, Ordering::Release);
|
||||||
// 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?!");
|
|
||||||
}
|
}
|
||||||
// remove this surface from the known ones (as well as any leftover dead surface)
|
// remove this surface from the known ones (as well as any leftover dead surface)
|
||||||
data.shell_data
|
data.shell_data
|
||||||
|
@ -683,14 +649,11 @@ where
|
||||||
* xdg_popup
|
* xdg_popup
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub(crate) fn send_popup_configure<R>(resource: &xdg_popup::XdgPopup, configure: PopupConfigure)
|
pub(crate) fn send_popup_configure(resource: &xdg_popup::XdgPopup, configure: PopupConfigure) {
|
||||||
where
|
|
||||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let data = resource
|
let data = resource
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let serial = configure.serial;
|
let serial = configure.serial;
|
||||||
|
@ -704,28 +667,20 @@ where
|
||||||
data.xdg_surface.configure(serial.into());
|
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
|
let data = resource
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
super::PopupSurface {
|
super::PopupSurface {
|
||||||
wl_surface: data.wl_surface.clone(),
|
wl_surface: data.wl_surface.clone(),
|
||||||
shell_surface: PopupKind::Xdg(resource.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)
|
fn xdg_popup_implementation(request: xdg_popup::Request, popup: xdg_popup::XdgPopup) {
|
||||||
where
|
let data = popup.as_ref().user_data().get::<ShellSurfaceUserData>().unwrap();
|
||||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let data = popup
|
|
||||||
.as_ref()
|
|
||||||
.user_data()
|
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
|
||||||
.unwrap();
|
|
||||||
match request {
|
match request {
|
||||||
xdg_popup::Request::Destroy => {
|
xdg_popup::Request::Destroy => {
|
||||||
// all is handled by our destructor
|
// all is handled by our destructor
|
||||||
|
@ -744,26 +699,10 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn destroy_popup<R>(popup: xdg_popup::XdgPopup)
|
fn destroy_popup(popup: xdg_popup::XdgPopup) {
|
||||||
where
|
let data = popup.as_ref().user_data().get::<ShellSurfaceUserData>().unwrap();
|
||||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
if let Some(data) = data.xdg_surface.as_ref().user_data().get::<XdgSurfaceUserData>() {
|
||||||
{
|
data.has_active_role.store(false, Ordering::Release);
|
||||||
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?!");
|
|
||||||
}
|
}
|
||||||
// remove this surface from the known ones (as well as any leftover dead surface)
|
// remove this surface from the known ones (as well as any leftover dead surface)
|
||||||
data.shell_data
|
data.shell_data
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::{cell::RefCell, ops::Deref as _, sync::Mutex};
|
use std::{cell::RefCell, ops::Deref as _, sync::Mutex};
|
||||||
|
|
||||||
use crate::wayland::compositor::{roles::*, CompositorToken};
|
use crate::wayland::compositor;
|
||||||
use crate::wayland::shell::xdg::{ConfigureError, PopupState};
|
use crate::wayland::shell::xdg::PopupState;
|
||||||
use crate::wayland::Serial;
|
use crate::wayland::Serial;
|
||||||
use wayland_protocols::{
|
use wayland_protocols::{
|
||||||
unstable::xdg_shell::v6::server::{
|
unstable::xdg_shell::v6::server::{
|
||||||
|
@ -15,25 +16,25 @@ use crate::utils::Rectangle;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
make_shell_client_data, PopupConfigure, PopupKind, PositionerState, ShellClient, ShellClientData,
|
make_shell_client_data, PopupConfigure, PopupKind, PositionerState, ShellClient, ShellClientData,
|
||||||
ShellData, ToplevelClientPending, ToplevelConfigure, ToplevelKind, XdgPopupSurfaceRole,
|
ShellData, SurfaceCachedState, ToplevelConfigure, ToplevelKind, XdgPopupSurfaceRoleAttributes,
|
||||||
XdgPopupSurfaceRoleAttributes, XdgRequest, XdgToplevelSurfaceRole, XdgToplevelSurfaceRoleAttributes,
|
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: Main<zxdg_shell_v6::ZxdgShellV6>,
|
||||||
shell_data: &ShellData<R>,
|
shell_data: &ShellData,
|
||||||
) -> zxdg_shell_v6::ZxdgShellV6
|
) -> zxdg_shell_v6::ZxdgShellV6 {
|
||||||
where
|
shell.quick_assign(|shell, req, _data| shell_implementation(req, shell.deref().clone()));
|
||||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
shell.quick_assign(|shell, req, _data| shell_implementation::<R>(req, shell.deref().clone()));
|
|
||||||
shell.as_ref().user_data().set(|| ShellUserData {
|
shell.as_ref().user_data().set(|| ShellUserData {
|
||||||
shell_data: shell_data.clone(),
|
shell_data: shell_data.clone(),
|
||||||
client_data: Mutex::new(make_shell_client_data()),
|
client_data: Mutex::new(make_shell_client_data()),
|
||||||
});
|
});
|
||||||
let mut user_impl = shell_data.user_impl.borrow_mut();
|
let mut user_impl = shell_data.user_impl.borrow_mut();
|
||||||
(&mut *user_impl)(XdgRequest::NewClient {
|
(&mut *user_impl)(XdgRequest::NewClient {
|
||||||
client: make_shell_client(&shell, shell_data.compositor_token),
|
client: make_shell_client(&shell),
|
||||||
});
|
});
|
||||||
shell.deref().clone()
|
shell.deref().clone()
|
||||||
}
|
}
|
||||||
|
@ -42,26 +43,19 @@ where
|
||||||
* xdg_shell
|
* xdg_shell
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub(crate) struct ShellUserData<R> {
|
pub(crate) struct ShellUserData {
|
||||||
shell_data: ShellData<R>,
|
shell_data: ShellData,
|
||||||
pub(crate) client_data: Mutex<ShellClientData>,
|
pub(crate) client_data: Mutex<ShellClientData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn make_shell_client<R>(
|
pub(crate) fn make_shell_client(resource: &zxdg_shell_v6::ZxdgShellV6) -> ShellClient {
|
||||||
resource: &zxdg_shell_v6::ZxdgShellV6,
|
|
||||||
token: CompositorToken<R>,
|
|
||||||
) -> ShellClient<R> {
|
|
||||||
ShellClient {
|
ShellClient {
|
||||||
kind: super::ShellClientKind::ZxdgV6(resource.clone()),
|
kind: super::ShellClientKind::ZxdgV6(resource.clone()),
|
||||||
_token: token,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shell_implementation<R>(request: zxdg_shell_v6::Request, shell: zxdg_shell_v6::ZxdgShellV6)
|
fn shell_implementation(request: zxdg_shell_v6::Request, shell: zxdg_shell_v6::ZxdgShellV6) {
|
||||||
where
|
let data = shell.as_ref().user_data().get::<ShellUserData>().unwrap();
|
||||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let data = shell.as_ref().user_data().get::<ShellUserData<R>>().unwrap();
|
|
||||||
match request {
|
match request {
|
||||||
zxdg_shell_v6::Request::Destroy => {
|
zxdg_shell_v6::Request::Destroy => {
|
||||||
// all is handled by destructor
|
// all is handled by destructor
|
||||||
|
@ -70,14 +64,13 @@ where
|
||||||
implement_positioner(id);
|
implement_positioner(id);
|
||||||
}
|
}
|
||||||
zxdg_shell_v6::Request::GetXdgSurface { id, surface } => {
|
zxdg_shell_v6::Request::GetXdgSurface { id, surface } => {
|
||||||
id.quick_assign(|surface, req, _data| {
|
id.quick_assign(|surface, req, _data| xdg_surface_implementation(req, surface.deref().clone()));
|
||||||
xdg_surface_implementation::<R>(req, surface.deref().clone())
|
id.assign_destructor(Filter::new(|surface, _, _data| destroy_surface(surface)));
|
||||||
});
|
|
||||||
id.assign_destructor(Filter::new(|surface, _, _data| destroy_surface::<R>(surface)));
|
|
||||||
id.as_ref().user_data().set(|| XdgSurfaceUserData {
|
id.as_ref().user_data().set(|| XdgSurfaceUserData {
|
||||||
shell_data: data.shell_data.clone(),
|
shell_data: data.shell_data.clone(),
|
||||||
wl_surface: surface,
|
wl_surface: surface,
|
||||||
shell: shell.clone(),
|
shell: shell.clone(),
|
||||||
|
has_active_role: AtomicBool::new(false),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
zxdg_shell_v6::Request::Pong { serial } => {
|
zxdg_shell_v6::Request::Pong { serial } => {
|
||||||
|
@ -94,7 +87,7 @@ where
|
||||||
if valid {
|
if valid {
|
||||||
let mut user_impl = data.shell_data.user_impl.borrow_mut();
|
let mut user_impl = data.shell_data.user_impl.borrow_mut();
|
||||||
(&mut *user_impl)(XdgRequest::ClientPong {
|
(&mut *user_impl)(XdgRequest::ClientPong {
|
||||||
client: make_shell_client(&shell, data.shell_data.compositor_token),
|
client: make_shell_client(&shell),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,21 +178,15 @@ fn implement_positioner(
|
||||||
* xdg_surface
|
* xdg_surface
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct XdgSurfaceUserData<R> {
|
struct XdgSurfaceUserData {
|
||||||
shell_data: ShellData<R>,
|
shell_data: ShellData,
|
||||||
wl_surface: wl_surface::WlSurface,
|
wl_surface: wl_surface::WlSurface,
|
||||||
shell: zxdg_shell_v6::ZxdgShellV6,
|
shell: zxdg_shell_v6::ZxdgShellV6,
|
||||||
|
has_active_role: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn destroy_surface<R>(surface: zxdg_surface_v6::ZxdgSurfaceV6)
|
fn destroy_surface(surface: zxdg_surface_v6::ZxdgSurfaceV6) {
|
||||||
where
|
let data = surface.as_ref().user_data().get::<XdgSurfaceUserData>().unwrap();
|
||||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let data = surface
|
|
||||||
.as_ref()
|
|
||||||
.user_data()
|
|
||||||
.get::<XdgSurfaceUserData<R>>()
|
|
||||||
.unwrap();
|
|
||||||
if !data.wl_surface.as_ref().is_alive() {
|
if !data.wl_surface.as_ref().is_alive() {
|
||||||
// the wl_surface is destroyed, this means the client is not
|
// the wl_surface is destroyed, this means the client is not
|
||||||
// trying to change the role but it's a cleanup (possibly a
|
// trying to change the role but it's a cleanup (possibly a
|
||||||
|
@ -207,25 +194,12 @@ where
|
||||||
return;
|
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.
|
// No role assigned to the surface, we can exit early.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let has_active_xdg_role = data
|
if data.has_active_role.load(Ordering::Acquire) {
|
||||||
.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 {
|
|
||||||
data.shell.as_ref().post_error(
|
data.shell.as_ref().post_error(
|
||||||
zxdg_shell_v6::Error::Role as u32,
|
zxdg_shell_v6::Error::Role as u32,
|
||||||
"xdg_surface was destroyed before its role object".into(),
|
"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,
|
request: zxdg_surface_v6::Request,
|
||||||
xdg_surface: zxdg_surface_v6::ZxdgSurfaceV6,
|
xdg_surface: zxdg_surface_v6::ZxdgSurfaceV6,
|
||||||
) where
|
) {
|
||||||
R: Role<XdgToplevelSurfaceRole> + Role<XdgPopupSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let data = xdg_surface
|
let data = xdg_surface
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<XdgSurfaceUserData<R>>()
|
.get::<XdgSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match request {
|
match request {
|
||||||
zxdg_surface_v6::Request::Destroy => {
|
zxdg_surface_v6::Request::Destroy => {
|
||||||
|
@ -253,14 +225,7 @@ fn xdg_surface_implementation<R>(
|
||||||
let surface = &data.wl_surface;
|
let surface = &data.wl_surface;
|
||||||
let shell = &data.shell;
|
let shell = &data.shell;
|
||||||
|
|
||||||
let role_data = XdgToplevelSurfaceRole::Some(Default::default());
|
if compositor::give_role(surface, ZXDG_TOPLEVEL_ROLE).is_err() {
|
||||||
|
|
||||||
if data
|
|
||||||
.shell_data
|
|
||||||
.compositor_token
|
|
||||||
.give_role_with(&surface, role_data)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
shell.as_ref().post_error(
|
shell.as_ref().post_error(
|
||||||
zxdg_shell_v6::Error::Role as u32,
|
zxdg_shell_v6::Error::Role as u32,
|
||||||
"Surface already has a role.".into(),
|
"Surface already has a role.".into(),
|
||||||
|
@ -268,10 +233,19 @@ fn xdg_surface_implementation<R>(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
id.quick_assign(|toplevel, req, _data| {
|
data.has_active_role.store(true, Ordering::Release);
|
||||||
toplevel_implementation::<R>(req, toplevel.deref().clone())
|
|
||||||
});
|
compositor::with_states(surface, |states| {
|
||||||
id.assign_destructor(Filter::new(|toplevel, _, _data| destroy_toplevel::<R>(toplevel)));
|
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 {
|
id.as_ref().user_data().set(|| ShellSurfaceUserData {
|
||||||
shell_data: data.shell_data.clone(),
|
shell_data: data.shell_data.clone(),
|
||||||
wl_surface: data.wl_surface.clone(),
|
wl_surface: data.wl_surface.clone(),
|
||||||
|
@ -304,11 +278,7 @@ fn xdg_surface_implementation<R>(
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
let parent_surface = {
|
let parent_surface = {
|
||||||
let parent_data = parent
|
let parent_data = parent.as_ref().user_data().get::<XdgSurfaceUserData>().unwrap();
|
||||||
.as_ref()
|
|
||||||
.user_data()
|
|
||||||
.get::<XdgSurfaceUserData<R>>()
|
|
||||||
.unwrap();
|
|
||||||
parent_data.wl_surface.clone()
|
parent_data.wl_surface.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -316,21 +286,15 @@ fn xdg_surface_implementation<R>(
|
||||||
let surface = &data.wl_surface;
|
let surface = &data.wl_surface;
|
||||||
let shell = &data.shell;
|
let shell = &data.shell;
|
||||||
|
|
||||||
let role_data = XdgPopupSurfaceRole::Some(XdgPopupSurfaceRoleAttributes {
|
let attributes = XdgPopupSurfaceRoleAttributes {
|
||||||
parent: Some(parent_surface),
|
parent: Some(parent_surface),
|
||||||
server_pending: Some(PopupState {
|
server_pending: Some(PopupState {
|
||||||
// Set the positioner data as the popup geometry
|
// Set the positioner data as the popup geometry
|
||||||
geometry: positioner_data.get_geometry(),
|
geometry: positioner_data.get_geometry(),
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
};
|
||||||
|
if compositor::give_role(surface, ZXDG_POPUP_ROLE).is_err() {
|
||||||
if data
|
|
||||||
.shell_data
|
|
||||||
.compositor_token
|
|
||||||
.give_role_with(&surface, role_data)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
shell.as_ref().post_error(
|
shell.as_ref().post_error(
|
||||||
zxdg_shell_v6::Error::Role as u32,
|
zxdg_shell_v6::Error::Role as u32,
|
||||||
"Surface already has a role.".into(),
|
"Surface already has a role.".into(),
|
||||||
|
@ -338,8 +302,25 @@ fn xdg_surface_implementation<R>(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
id.quick_assign(|popup, req, _data| popup_implementation::<R>(req, popup.deref().clone()));
|
data.has_active_role.store(true, Ordering::Release);
|
||||||
id.assign_destructor(Filter::new(|popup, _, _data| destroy_popup::<R>(popup)));
|
|
||||||
|
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 {
|
id.as_ref().user_data().set(|| ShellSurfaceUserData {
|
||||||
shell_data: data.shell_data.clone(),
|
shell_data: data.shell_data.clone(),
|
||||||
wl_surface: data.wl_surface.clone(),
|
wl_surface: data.wl_surface.clone(),
|
||||||
|
@ -364,33 +345,28 @@ fn xdg_surface_implementation<R>(
|
||||||
// which is a protocol error.
|
// which is a protocol error.
|
||||||
let surface = &data.wl_surface;
|
let surface = &data.wl_surface;
|
||||||
|
|
||||||
if !data.shell_data.compositor_token.has_a_role(surface) {
|
let role = compositor::get_role(surface);
|
||||||
data.shell.as_ref().post_error(
|
|
||||||
|
if role.is_none() {
|
||||||
|
xdg_surface.as_ref().post_error(
|
||||||
zxdg_surface_v6::Error::NotConstructed as u32,
|
zxdg_surface_v6::Error::NotConstructed as u32,
|
||||||
"xdg_surface must have a role.".into(),
|
"xdg_surface must have a role.".into(),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the next window geometry here, the geometry will be moved from
|
if role != Some(ZXDG_TOPLEVEL_ROLE) && role != Some(ZXDG_POPUP_ROLE) {
|
||||||
// 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 {
|
|
||||||
data.shell.as_ref().post_error(
|
data.shell.as_ref().post_error(
|
||||||
zxdg_shell_v6::Error::Role as u32,
|
zxdg_shell_v6::Error::Role as u32,
|
||||||
"xdg_surface must have a role of xdg_toplevel or xdg_popup.".into(),
|
"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 } => {
|
zxdg_surface_v6::Request::AckConfigure { serial } => {
|
||||||
let serial = Serial::from(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
|
// 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
|
// or xdg_popup. If none of the role matches the xdg_surface has no role set
|
||||||
// which is a protocol error.
|
// 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(
|
data.shell.as_ref().post_error(
|
||||||
zxdg_surface_v6::Error::NotConstructed as u32,
|
zxdg_surface_v6::Error::NotConstructed as u32,
|
||||||
"xdg_surface must have a role.".into(),
|
"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
|
// This can be used to integrate custom protocol extensions
|
||||||
//
|
//
|
||||||
let configure = match data
|
let found_configure = compositor::with_states(surface, |states| {
|
||||||
.shell_data
|
if states.role == Some(ZXDG_TOPLEVEL_ROLE) {
|
||||||
.compositor_token
|
Ok(states
|
||||||
.with_xdg_role(surface, |role| role.ack_configure(serial))
|
.data_map
|
||||||
{
|
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
|
||||||
Ok(Ok(configure)) => configure,
|
.unwrap()
|
||||||
Ok(Err(ConfigureError::SerialNotFound(serial))) => {
|
.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(
|
data.shell.as_ref().post_error(
|
||||||
zxdg_shell_v6::Error::InvalidSurfaceState as u32,
|
zxdg_shell_v6::Error::InvalidSurfaceState as u32,
|
||||||
format!("wrong configure serial: {}", <u32>::from(serial)),
|
format!("wrong configure serial: {}", <u32>::from(serial)),
|
||||||
|
@ -456,64 +450,54 @@ fn xdg_surface_implementation<R>(
|
||||||
* xdg_toplevel
|
* xdg_toplevel
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub struct ShellSurfaceUserData<R> {
|
pub struct ShellSurfaceUserData {
|
||||||
pub(crate) shell_data: ShellData<R>,
|
pub(crate) shell_data: ShellData,
|
||||||
pub(crate) wl_surface: wl_surface::WlSurface,
|
pub(crate) wl_surface: wl_surface::WlSurface,
|
||||||
pub(crate) shell: zxdg_shell_v6::ZxdgShellV6,
|
pub(crate) shell: zxdg_shell_v6::ZxdgShellV6,
|
||||||
pub(crate) xdg_surface: zxdg_surface_v6::ZxdgSurfaceV6,
|
pub(crate) xdg_surface: zxdg_surface_v6::ZxdgSurfaceV6,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility functions allowing to factor out a lot of the upcoming logic
|
// Utility functions allowing to factor out a lot of the upcoming logic
|
||||||
fn with_surface_toplevel_role_data<R, F, T>(
|
fn with_surface_toplevel_role_data<F, T>(toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, f: F) -> T
|
||||||
shell_data: &ShellData<R>,
|
|
||||||
toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6,
|
|
||||||
f: F,
|
|
||||||
) -> T
|
|
||||||
where
|
where
|
||||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
|
||||||
F: FnOnce(&mut XdgToplevelSurfaceRoleAttributes) -> T,
|
F: FnOnce(&mut XdgToplevelSurfaceRoleAttributes) -> T,
|
||||||
{
|
{
|
||||||
let data = toplevel
|
let data = toplevel
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
shell_data
|
compositor::with_states(&data.wl_surface, |states| {
|
||||||
.compositor_token
|
f(&mut *states
|
||||||
.with_role_data::<XdgToplevelSurfaceRole, _, _>(&data.wl_surface, |role| {
|
.data_map
|
||||||
let attributes = role
|
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
|
||||||
.as_mut()
|
.unwrap()
|
||||||
.expect("xdg_toplevel exists but role has been destroyed?!");
|
.lock()
|
||||||
f(attributes)
|
.unwrap())
|
||||||
})
|
})
|
||||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!")
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_surface_toplevel_client_pending<R, F, T>(
|
fn with_toplevel_pending_state<F, T>(toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, f: F) -> T
|
||||||
shell_data: &ShellData<R>,
|
|
||||||
toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6,
|
|
||||||
f: F,
|
|
||||||
) -> T
|
|
||||||
where
|
where
|
||||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
F: FnOnce(&mut SurfaceCachedState) -> T,
|
||||||
F: FnOnce(&mut ToplevelClientPending) -> T,
|
|
||||||
{
|
{
|
||||||
with_surface_toplevel_role_data(shell_data, toplevel, |data| {
|
let data = toplevel
|
||||||
if data.client_pending.is_none() {
|
.as_ref()
|
||||||
data.client_pending = Some(Default::default());
|
.user_data()
|
||||||
}
|
.get::<ShellSurfaceUserData>()
|
||||||
f(&mut data.client_pending.as_mut().unwrap())
|
.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)
|
pub fn send_toplevel_configure(resource: &zxdg_toplevel_v6::ZxdgToplevelV6, configure: ToplevelConfigure) {
|
||||||
where
|
|
||||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let data = resource
|
let data = resource
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (width, height) = configure.state.size.unwrap_or((0, 0));
|
let (width, height) = configure.state.size.unwrap_or((0, 0));
|
||||||
|
@ -536,41 +520,35 @@ where
|
||||||
data.xdg_surface.configure(serial.into());
|
data.xdg_surface.configure(serial.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_toplevel_handle<R: 'static>(
|
fn make_toplevel_handle(resource: &zxdg_toplevel_v6::ZxdgToplevelV6) -> super::ToplevelSurface {
|
||||||
resource: &zxdg_toplevel_v6::ZxdgToplevelV6,
|
|
||||||
) -> super::ToplevelSurface<R> {
|
|
||||||
let data = resource
|
let data = resource
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
super::ToplevelSurface {
|
super::ToplevelSurface {
|
||||||
wl_surface: data.wl_surface.clone(),
|
wl_surface: data.wl_surface.clone(),
|
||||||
shell_surface: ToplevelKind::ZxdgV6(resource.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)
|
fn toplevel_implementation(request: zxdg_toplevel_v6::Request, toplevel: zxdg_toplevel_v6::ZxdgToplevelV6) {
|
||||||
where
|
|
||||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let data = toplevel
|
let data = toplevel
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match request {
|
match request {
|
||||||
zxdg_toplevel_v6::Request::Destroy => {
|
zxdg_toplevel_v6::Request::Destroy => {
|
||||||
// all it done by the destructor
|
// all it done by the destructor
|
||||||
}
|
}
|
||||||
zxdg_toplevel_v6::Request::SetParent { parent } => {
|
zxdg_toplevel_v6::Request::SetParent { parent } => {
|
||||||
with_surface_toplevel_role_data(&data.shell_data, &toplevel, |data| {
|
with_surface_toplevel_role_data(&toplevel, |data| {
|
||||||
data.parent = parent.map(|toplevel_surface_parent| {
|
data.parent = parent.map(|toplevel_surface_parent| {
|
||||||
let parent_data = toplevel_surface_parent
|
let parent_data = toplevel_surface_parent
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
parent_data.wl_surface.clone()
|
parent_data.wl_surface.clone()
|
||||||
})
|
})
|
||||||
|
@ -578,13 +556,13 @@ where
|
||||||
}
|
}
|
||||||
zxdg_toplevel_v6::Request::SetTitle { title } => {
|
zxdg_toplevel_v6::Request::SetTitle { title } => {
|
||||||
// Title is not double buffered, we can set it directly
|
// 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);
|
data.title = Some(title);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
zxdg_toplevel_v6::Request::SetAppId { app_id } => {
|
zxdg_toplevel_v6::Request::SetAppId { app_id } => {
|
||||||
// AppId is not double buffered, we can set it directly
|
// 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);
|
role.app_id = Some(app_id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -623,13 +601,13 @@ where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
zxdg_toplevel_v6::Request::SetMaxSize { width, height } => {
|
zxdg_toplevel_v6::Request::SetMaxSize { width, height } => {
|
||||||
with_surface_toplevel_client_pending(&data.shell_data, &toplevel, |toplevel_data| {
|
with_toplevel_pending_state(&toplevel, |toplevel_data| {
|
||||||
toplevel_data.max_size = Some((width, height));
|
toplevel_data.max_size = (width, height);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
zxdg_toplevel_v6::Request::SetMinSize { width, height } => {
|
zxdg_toplevel_v6::Request::SetMinSize { width, height } => {
|
||||||
with_surface_toplevel_client_pending(&data.shell_data, &toplevel, |toplevel_data| {
|
with_toplevel_pending_state(&toplevel, |toplevel_data| {
|
||||||
toplevel_data.min_size = Some((width, height));
|
toplevel_data.min_size = (width, height);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
zxdg_toplevel_v6::Request::SetMaximized => {
|
zxdg_toplevel_v6::Request::SetMaximized => {
|
||||||
|
@ -666,26 +644,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn destroy_toplevel<R>(toplevel: zxdg_toplevel_v6::ZxdgToplevelV6)
|
fn destroy_toplevel(toplevel: zxdg_toplevel_v6::ZxdgToplevelV6) {
|
||||||
where
|
|
||||||
R: Role<XdgToplevelSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let data = toplevel
|
let data = toplevel
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if !data.wl_surface.as_ref().is_alive() {
|
if let Some(data) = data.xdg_surface.as_ref().user_data().get::<XdgSurfaceUserData>() {
|
||||||
// the wl_surface is destroyed, this means the client is not
|
data.has_active_role.store(false, Ordering::Release);
|
||||||
// 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?!");
|
|
||||||
}
|
}
|
||||||
// remove this surface from the known ones (as well as any leftover dead surface)
|
// remove this surface from the known ones (as well as any leftover dead surface)
|
||||||
data.shell_data
|
data.shell_data
|
||||||
|
@ -700,14 +666,11 @@ where
|
||||||
* xdg_popup
|
* xdg_popup
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub(crate) fn send_popup_configure<R>(resource: &zxdg_popup_v6::ZxdgPopupV6, configure: PopupConfigure)
|
pub(crate) fn send_popup_configure(resource: &zxdg_popup_v6::ZxdgPopupV6, configure: PopupConfigure) {
|
||||||
where
|
|
||||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let data = resource
|
let data = resource
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let serial = configure.serial;
|
let serial = configure.serial;
|
||||||
|
@ -721,28 +684,20 @@ where
|
||||||
data.xdg_surface.configure(serial.into());
|
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
|
let data = resource
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
.get::<ShellSurfaceUserData>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
super::PopupSurface {
|
super::PopupSurface {
|
||||||
wl_surface: data.wl_surface.clone(),
|
wl_surface: data.wl_surface.clone(),
|
||||||
shell_surface: PopupKind::ZxdgV6(resource.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)
|
fn popup_implementation(request: zxdg_popup_v6::Request, popup: zxdg_popup_v6::ZxdgPopupV6) {
|
||||||
where
|
let data = popup.as_ref().user_data().get::<ShellSurfaceUserData>().unwrap();
|
||||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
|
||||||
{
|
|
||||||
let data = popup
|
|
||||||
.as_ref()
|
|
||||||
.user_data()
|
|
||||||
.get::<ShellSurfaceUserData<R>>()
|
|
||||||
.unwrap();
|
|
||||||
match request {
|
match request {
|
||||||
zxdg_popup_v6::Request::Destroy => {
|
zxdg_popup_v6::Request::Destroy => {
|
||||||
// all is handled by our destructor
|
// all is handled by our destructor
|
||||||
|
@ -761,26 +716,10 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn destroy_popup<R>(popup: zxdg_popup_v6::ZxdgPopupV6)
|
fn destroy_popup(popup: zxdg_popup_v6::ZxdgPopupV6) {
|
||||||
where
|
let data = popup.as_ref().user_data().get::<ShellSurfaceUserData>().unwrap();
|
||||||
R: Role<XdgPopupSurfaceRole> + 'static,
|
if let Some(data) = data.xdg_surface.as_ref().user_data().get::<XdgSurfaceUserData>() {
|
||||||
{
|
data.has_active_role.store(false, Ordering::Release);
|
||||||
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?!");
|
|
||||||
}
|
}
|
||||||
// remove this surface from the known ones (as well as any leftover dead surface)
|
// remove this surface from the known ones (as well as any leftover dead surface)
|
||||||
data.shell_data
|
data.shell_data
|
||||||
|
|
Loading…
Reference in New Issue