Merge pull request #354 from PolyMeilex/layer-v3
WLR Layer Shell Support
This commit is contained in:
commit
01ac0f135b
|
@ -6,9 +6,10 @@
|
||||||
|
|
||||||
#### Clients & Protocols
|
#### Clients & Protocols
|
||||||
|
|
||||||
|
- `wlr-layer-shell-unstable-v1` support
|
||||||
- Added public api constants for the roles of `wl_shell_surface`, `zxdg_toplevel` and `xdg_toplevel`. See the
|
- Added public api constants for the roles of `wl_shell_surface`, `zxdg_toplevel` and `xdg_toplevel`. See the
|
||||||
`shell::legacy` and `shell::xdg` modules for these constants.
|
`shell::legacy` and `shell::xdg` modules for these constants.
|
||||||
- Whether a surface is toplevel equivalent can be determined with the new function `shell::is_toplevel_equivalent`.
|
- Whether a surface is toplevel equivalent can be determined with the new function `shell::is_toplevel_equivalent`.
|
||||||
- Setting the parent of a toplevel surface is now possible with the `xdg::ToplevelSurface::set_parent` function.
|
- Setting the parent of a toplevel surface is now possible with the `xdg::ToplevelSurface::set_parent` function.
|
||||||
- Add support for the zxdg-foreign-v2 protocol.
|
- Add support for the zxdg-foreign-v2 protocol.
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ use smithay::{
|
||||||
SurfaceAttributes, TraversalAction,
|
SurfaceAttributes, TraversalAction,
|
||||||
},
|
},
|
||||||
seat::CursorImageAttributes,
|
seat::CursorImageAttributes,
|
||||||
|
shell::wlr_layer::Layer,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -241,6 +242,59 @@ where
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn draw_layers<R, E, F, T>(
|
||||||
|
renderer: &mut R,
|
||||||
|
frame: &mut F,
|
||||||
|
window_map: &WindowMap,
|
||||||
|
layer: Layer,
|
||||||
|
output_rect: Rectangle<i32, Logical>,
|
||||||
|
output_scale: f32,
|
||||||
|
log: &::slog::Logger,
|
||||||
|
) -> Result<(), SwapBuffersError>
|
||||||
|
where
|
||||||
|
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportAll,
|
||||||
|
F: Frame<Error = E, TextureId = T>,
|
||||||
|
E: std::error::Error + Into<SwapBuffersError>,
|
||||||
|
T: Texture + 'static,
|
||||||
|
{
|
||||||
|
let mut result = Ok(());
|
||||||
|
|
||||||
|
window_map
|
||||||
|
.layers
|
||||||
|
.with_layers_from_bottom_to_top(&layer, |layer_surface| {
|
||||||
|
// skip layers that do not overlap with a given output
|
||||||
|
if !output_rect.overlaps(layer_surface.bbox) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut initial_place: Point<i32, Logical> = layer_surface.location;
|
||||||
|
initial_place.x -= output_rect.loc.x;
|
||||||
|
|
||||||
|
if let Some(wl_surface) = layer_surface.surface.get_surface() {
|
||||||
|
// this surface is a root of a subsurface tree that needs to be drawn
|
||||||
|
if let Err(err) =
|
||||||
|
draw_surface_tree(renderer, frame, wl_surface, initial_place, output_scale, log)
|
||||||
|
{
|
||||||
|
result = Err(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
window_map.with_child_popups(wl_surface, |popup| {
|
||||||
|
let location = popup.location();
|
||||||
|
let draw_location = initial_place + location;
|
||||||
|
if let Some(wl_surface) = popup.get_surface() {
|
||||||
|
if let Err(err) =
|
||||||
|
draw_surface_tree(renderer, frame, wl_surface, draw_location, output_scale, log)
|
||||||
|
{
|
||||||
|
result = Err(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
pub fn draw_dnd_icon<R, E, F, T>(
|
pub fn draw_dnd_icon<R, E, F, T>(
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
frame: &mut F,
|
frame: &mut F,
|
||||||
|
|
|
@ -211,6 +211,11 @@ impl AnvilState<WinitData> {
|
||||||
},
|
},
|
||||||
crate::winit::OUTPUT_NAME,
|
crate::winit::OUTPUT_NAME,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let output_mut = self.output_map.borrow();
|
||||||
|
let output = output_mut.find_by_name(crate::winit::OUTPUT_NAME).unwrap();
|
||||||
|
|
||||||
|
self.window_map.borrow_mut().layers.arange_layers(output);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// other events are not handled in anvil (yet)
|
// other events are not handled in anvil (yet)
|
||||||
|
|
|
@ -4,7 +4,10 @@ use smithay::{
|
||||||
reexports::{
|
reexports::{
|
||||||
wayland_protocols::xdg_shell::server::xdg_toplevel,
|
wayland_protocols::xdg_shell::server::xdg_toplevel,
|
||||||
wayland_server::{
|
wayland_server::{
|
||||||
protocol::{wl_output, wl_surface::WlSurface},
|
protocol::{
|
||||||
|
wl_output,
|
||||||
|
wl_surface::{self, WlSurface},
|
||||||
|
},
|
||||||
Display, Global, UserDataMap,
|
Display, Global, UserDataMap,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -22,6 +25,7 @@ pub struct Output {
|
||||||
output: output::Output,
|
output: output::Output,
|
||||||
global: Option<Global<wl_output::WlOutput>>,
|
global: Option<Global<wl_output::WlOutput>>,
|
||||||
surfaces: Vec<WlSurface>,
|
surfaces: Vec<WlSurface>,
|
||||||
|
layer_surfaces: RefCell<Vec<wl_surface::WlSurface>>,
|
||||||
current_mode: Mode,
|
current_mode: Mode,
|
||||||
scale: f32,
|
scale: f32,
|
||||||
output_scale: i32,
|
output_scale: i32,
|
||||||
|
@ -60,6 +64,7 @@ impl Output {
|
||||||
output,
|
output,
|
||||||
location,
|
location,
|
||||||
surfaces: Vec::new(),
|
surfaces: Vec::new(),
|
||||||
|
layer_surfaces: Default::default(),
|
||||||
current_mode: mode,
|
current_mode: mode,
|
||||||
scale,
|
scale,
|
||||||
output_scale,
|
output_scale,
|
||||||
|
@ -101,6 +106,16 @@ impl Output {
|
||||||
pub fn current_mode(&self) -> Mode {
|
pub fn current_mode(&self) -> Mode {
|
||||||
self.current_mode
|
self.current_mode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a layer surface to this output
|
||||||
|
pub fn add_layer_surface(&self, layer: wl_surface::WlSurface) {
|
||||||
|
self.layer_surfaces.borrow_mut().push(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all layer surfaces assigned to this output
|
||||||
|
pub fn layer_surfaces(&self) -> Vec<wl_surface::WlSurface> {
|
||||||
|
self.layer_surfaces.borrow().iter().cloned().collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Output {
|
impl Drop for Output {
|
||||||
|
@ -171,6 +186,7 @@ impl OutputMap {
|
||||||
// and move them to the primary output
|
// and move them to the primary output
|
||||||
let primary_output_location = self.with_primary().map(|o| o.location()).unwrap_or_default();
|
let primary_output_location = self.with_primary().map(|o| o.location()).unwrap_or_default();
|
||||||
let mut window_map = self.window_map.borrow_mut();
|
let mut window_map = self.window_map.borrow_mut();
|
||||||
|
|
||||||
// TODO: This is a bit unfortunate, we save the windows in a temp vector
|
// TODO: This is a bit unfortunate, we save the windows in a temp vector
|
||||||
// cause we can not call window_map.set_location within the closure.
|
// cause we can not call window_map.set_location within the closure.
|
||||||
let mut windows_to_move = Vec::new();
|
let mut windows_to_move = Vec::new();
|
||||||
|
@ -218,6 +234,10 @@ impl OutputMap {
|
||||||
for (window, location) in windows_to_move.drain(..) {
|
for (window, location) in windows_to_move.drain(..) {
|
||||||
window_map.set_location(&window, location);
|
window_map.set_location(&window, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for output in self.outputs.iter() {
|
||||||
|
window_map.layers.arange_layers(output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add<N>(&mut self, name: N, physical: PhysicalProperties, mode: Mode) -> &Output
|
pub fn add<N>(&mut self, name: N, physical: PhysicalProperties, mode: Mode) -> &Output
|
||||||
|
@ -293,6 +313,15 @@ impl OutputMap {
|
||||||
self.find(|o| o.output.owns(output))
|
self.find(|o| o.output.owns(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find_by_layer_surface(&self, surface: &wl_surface::WlSurface) -> Option<&Output> {
|
||||||
|
self.find(|o| {
|
||||||
|
o.layer_surfaces
|
||||||
|
.borrow()
|
||||||
|
.iter()
|
||||||
|
.any(|s| s.as_ref().equals(surface.as_ref()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn find_by_name<N>(&self, name: N) -> Option<&Output>
|
pub fn find_by_name<N>(&self, name: N) -> Option<&Output>
|
||||||
where
|
where
|
||||||
N: AsRef<str>,
|
N: AsRef<str>,
|
||||||
|
@ -388,9 +417,10 @@ impl OutputMap {
|
||||||
|
|
||||||
pub fn refresh(&mut self) {
|
pub fn refresh(&mut self) {
|
||||||
// Clean-up dead surfaces
|
// Clean-up dead surfaces
|
||||||
self.outputs
|
self.outputs.iter_mut().for_each(|o| {
|
||||||
.iter_mut()
|
o.surfaces.retain(|s| s.as_ref().is_alive());
|
||||||
.for_each(|o| o.surfaces.retain(|s| s.as_ref().is_alive()));
|
o.layer_surfaces.borrow_mut().retain(|s| s.as_ref().is_alive());
|
||||||
|
});
|
||||||
|
|
||||||
let window_map = self.window_map.clone();
|
let window_map = self.window_map.clone();
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ use smithay::{
|
||||||
seat::{AxisFrame, GrabStartData, PointerGrab, PointerInnerHandle, Seat},
|
seat::{AxisFrame, GrabStartData, PointerGrab, PointerInnerHandle, Seat},
|
||||||
shell::{
|
shell::{
|
||||||
legacy::{wl_shell_init, ShellRequest, ShellState as WlShellState, ShellSurfaceKind},
|
legacy::{wl_shell_init, ShellRequest, ShellState as WlShellState, ShellSurfaceKind},
|
||||||
|
wlr_layer::{LayerShellRequest, LayerSurfaceAttributes},
|
||||||
xdg::{
|
xdg::{
|
||||||
xdg_shell_init, Configure, ShellState as XdgShellState, SurfaceCachedState,
|
xdg_shell_init, Configure, ShellState as XdgShellState, SurfaceCachedState,
|
||||||
XdgPopupSurfaceRoleAttributes, XdgRequest, XdgToplevelSurfaceRoleAttributes,
|
XdgPopupSurfaceRoleAttributes, XdgRequest, XdgToplevelSurfaceRoleAttributes,
|
||||||
|
@ -329,7 +330,8 @@ pub fn init_shell<BackendData: 'static>(display: Rc<RefCell<Display>>, log: ::sl
|
||||||
move |surface, mut ddata| {
|
move |surface, mut ddata| {
|
||||||
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, &*window_map)
|
let output_map = anvil_state.output_map.as_ref();
|
||||||
|
surface_commit(&surface, &*window_map, &*output_map)
|
||||||
},
|
},
|
||||||
log.clone(),
|
log.clone(),
|
||||||
);
|
);
|
||||||
|
@ -822,6 +824,38 @@ pub fn init_shell<BackendData: 'static>(display: Rc<RefCell<Display>>, log: ::sl
|
||||||
log.clone(),
|
log.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let layer_window_map = window_map.clone();
|
||||||
|
let layer_output_map = output_map.clone();
|
||||||
|
smithay::wayland::shell::wlr_layer::wlr_layer_shell_init(
|
||||||
|
&mut *display.borrow_mut(),
|
||||||
|
move |event, mut ddata| match event {
|
||||||
|
LayerShellRequest::NewLayerSurface {
|
||||||
|
surface,
|
||||||
|
output,
|
||||||
|
layer,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let output_map = layer_output_map.borrow();
|
||||||
|
let anvil_state = ddata.get::<AnvilState<BackendData>>().unwrap();
|
||||||
|
|
||||||
|
let output = output.and_then(|output| output_map.find_by_output(&output));
|
||||||
|
let output = output.unwrap_or_else(|| {
|
||||||
|
output_map
|
||||||
|
.find_by_position(anvil_state.pointer_location.to_i32_round())
|
||||||
|
.unwrap_or_else(|| output_map.with_primary().unwrap())
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(wl_surface) = surface.get_surface() {
|
||||||
|
output.add_layer_surface(wl_surface.clone());
|
||||||
|
|
||||||
|
layer_window_map.borrow_mut().layers.insert(surface, layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LayerShellRequest::AckConfigure { .. } => {}
|
||||||
|
},
|
||||||
|
log.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
ShellHandles {
|
ShellHandles {
|
||||||
xdg_state: xdg_shell_state,
|
xdg_state: xdg_shell_state,
|
||||||
wl_state: wl_shell_state,
|
wl_state: wl_shell_state,
|
||||||
|
@ -937,7 +971,11 @@ impl SurfaceData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn surface_commit(surface: &wl_surface::WlSurface, window_map: &RefCell<WindowMap>) {
|
fn surface_commit(
|
||||||
|
surface: &wl_surface::WlSurface,
|
||||||
|
window_map: &RefCell<WindowMap>,
|
||||||
|
output_map: &RefCell<OutputMap>,
|
||||||
|
) {
|
||||||
#[cfg(feature = "xwayland")]
|
#[cfg(feature = "xwayland")]
|
||||||
super::xwayland::commit_hook(surface);
|
super::xwayland::commit_hook(surface);
|
||||||
|
|
||||||
|
@ -1055,4 +1093,25 @@ fn surface_commit(surface: &wl_surface::WlSurface, window_map: &RefCell<WindowMa
|
||||||
popup.send_configure();
|
popup.send_configure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(layer) = window_map.layers.find(surface) {
|
||||||
|
// send the initial configure if relevant
|
||||||
|
let initial_configure_sent = with_states(surface, |states| {
|
||||||
|
states
|
||||||
|
.data_map
|
||||||
|
.get::<Mutex<LayerSurfaceAttributes>>()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.initial_configure_sent
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
if !initial_configure_sent {
|
||||||
|
layer.surface.send_configure();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(output) = output_map.borrow().find_by_layer_surface(surface) {
|
||||||
|
window_map.layers.arange_layers(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@ use smithay::{
|
||||||
wayland::{
|
wayland::{
|
||||||
output::{Mode, PhysicalProperties},
|
output::{Mode, PhysicalProperties},
|
||||||
seat::CursorImageStatus,
|
seat::CursorImageStatus,
|
||||||
|
shell::wlr_layer::Layer,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
|
@ -744,9 +745,34 @@ fn render_surface(
|
||||||
Transform::Flipped180, // Scanout is rotated
|
Transform::Flipped180, // Scanout is rotated
|
||||||
|renderer, frame| {
|
|renderer, frame| {
|
||||||
frame.clear([0.8, 0.8, 0.9, 1.0])?;
|
frame.clear([0.8, 0.8, 0.9, 1.0])?;
|
||||||
|
|
||||||
|
for layer in [Layer::Background, Layer::Bottom] {
|
||||||
|
draw_layers(
|
||||||
|
renderer,
|
||||||
|
frame,
|
||||||
|
window_map,
|
||||||
|
layer,
|
||||||
|
output_geometry,
|
||||||
|
output_scale,
|
||||||
|
logger,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
// draw the surfaces
|
// draw the surfaces
|
||||||
draw_windows(renderer, frame, window_map, output_geometry, output_scale, logger)?;
|
draw_windows(renderer, frame, window_map, output_geometry, output_scale, logger)?;
|
||||||
|
|
||||||
|
for layer in [Layer::Top, Layer::Overlay] {
|
||||||
|
draw_layers(
|
||||||
|
renderer,
|
||||||
|
frame,
|
||||||
|
window_map,
|
||||||
|
layer,
|
||||||
|
output_geometry,
|
||||||
|
output_scale,
|
||||||
|
logger,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
// set cursor
|
// set cursor
|
||||||
if output_geometry.to_f64().contains(pointer_location) {
|
if output_geometry.to_f64().contains(pointer_location) {
|
||||||
let (ptr_x, ptr_y) = pointer_location.into();
|
let (ptr_x, ptr_y) = pointer_location.into();
|
||||||
|
|
|
@ -8,6 +8,7 @@ use smithay::{
|
||||||
compositor::{with_states, with_surface_tree_downward, SubsurfaceCachedState, TraversalAction},
|
compositor::{with_states, with_surface_tree_downward, SubsurfaceCachedState, TraversalAction},
|
||||||
shell::{
|
shell::{
|
||||||
legacy::ShellSurface,
|
legacy::ShellSurface,
|
||||||
|
wlr_layer::Layer,
|
||||||
xdg::{PopupSurface, SurfaceCachedState, ToplevelSurface, XdgPopupSurfaceRoleAttributes},
|
xdg::{PopupSurface, SurfaceCachedState, ToplevelSurface, XdgPopupSurfaceRoleAttributes},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -17,6 +18,9 @@ use crate::shell::SurfaceData;
|
||||||
#[cfg(feature = "xwayland")]
|
#[cfg(feature = "xwayland")]
|
||||||
use crate::xwayland::X11Surface;
|
use crate::xwayland::X11Surface;
|
||||||
|
|
||||||
|
mod layer_map;
|
||||||
|
pub use layer_map::{LayerMap, LayerSurface};
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum Kind {
|
pub enum Kind {
|
||||||
Xdg(ToplevelSurface),
|
Xdg(ToplevelSurface),
|
||||||
|
@ -242,6 +246,8 @@ pub struct Popup {
|
||||||
pub struct WindowMap {
|
pub struct WindowMap {
|
||||||
windows: Vec<Window>,
|
windows: Vec<Window>,
|
||||||
popups: Vec<Popup>,
|
popups: Vec<Popup>,
|
||||||
|
|
||||||
|
pub layers: LayerMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowMap {
|
impl WindowMap {
|
||||||
|
@ -268,11 +274,26 @@ impl WindowMap {
|
||||||
&self,
|
&self,
|
||||||
point: Point<f64, Logical>,
|
point: Point<f64, Logical>,
|
||||||
) -> Option<(wl_surface::WlSurface, Point<i32, Logical>)> {
|
) -> Option<(wl_surface::WlSurface, Point<i32, Logical>)> {
|
||||||
|
if let Some(res) = self.layers.get_surface_under(&Layer::Overlay, point) {
|
||||||
|
return Some(res);
|
||||||
|
}
|
||||||
|
if let Some(res) = self.layers.get_surface_under(&Layer::Top, point) {
|
||||||
|
return Some(res);
|
||||||
|
}
|
||||||
|
|
||||||
for w in &self.windows {
|
for w in &self.windows {
|
||||||
if let Some(surface) = w.matching(point) {
|
if let Some(surface) = w.matching(point) {
|
||||||
return Some(surface);
|
return Some(surface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(res) = self.layers.get_surface_under(&Layer::Bottom, point) {
|
||||||
|
return Some(res);
|
||||||
|
}
|
||||||
|
if let Some(res) = self.layers.get_surface_under(&Layer::Background, point) {
|
||||||
|
return Some(res);
|
||||||
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,6 +351,7 @@ impl WindowMap {
|
||||||
pub fn refresh(&mut self) {
|
pub fn refresh(&mut self) {
|
||||||
self.windows.retain(|w| w.toplevel.alive());
|
self.windows.retain(|w| w.toplevel.alive());
|
||||||
self.popups.retain(|p| p.popup.alive());
|
self.popups.retain(|p| p.popup.alive());
|
||||||
|
self.layers.refresh();
|
||||||
for w in &mut self.windows {
|
for w in &mut self.windows {
|
||||||
w.self_update();
|
w.self_update();
|
||||||
}
|
}
|
||||||
|
@ -404,5 +426,6 @@ impl WindowMap {
|
||||||
for window in &self.windows {
|
for window in &self.windows {
|
||||||
window.send_frame(time);
|
window.send_frame(time);
|
||||||
}
|
}
|
||||||
|
self.layers.send_frames(time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,253 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
use smithay::{
|
||||||
|
reexports::wayland_server::protocol::wl_surface,
|
||||||
|
utils::{Logical, Point, Rectangle},
|
||||||
|
wayland::{
|
||||||
|
compositor::{with_states, with_surface_tree_downward, SubsurfaceCachedState, TraversalAction},
|
||||||
|
shell::wlr_layer::{self, Anchor, LayerSurfaceCachedState},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{output_map::Output, shell::SurfaceData};
|
||||||
|
|
||||||
|
pub struct LayerSurface {
|
||||||
|
pub surface: wlr_layer::LayerSurface,
|
||||||
|
pub location: Point<i32, Logical>,
|
||||||
|
pub bbox: Rectangle<i32, Logical>,
|
||||||
|
pub layer: wlr_layer::Layer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayerSurface {
|
||||||
|
/// Finds the topmost surface under this point if any and returns it together with the location of this
|
||||||
|
/// surface.
|
||||||
|
fn matching(&self, point: Point<f64, Logical>) -> Option<(wl_surface::WlSurface, Point<i32, Logical>)> {
|
||||||
|
if !self.bbox.to_f64().contains(point) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// need to check more carefully
|
||||||
|
let found = RefCell::new(None);
|
||||||
|
if let Some(wl_surface) = self.surface.get_surface() {
|
||||||
|
with_surface_tree_downward(
|
||||||
|
wl_surface,
|
||||||
|
self.location,
|
||||||
|
|wl_surface, states, location| {
|
||||||
|
let mut location = *location;
|
||||||
|
let data = states.data_map.get::<RefCell<SurfaceData>>();
|
||||||
|
|
||||||
|
if states.role == Some("subsurface") {
|
||||||
|
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
||||||
|
location += current.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
let contains_the_point = data
|
||||||
|
.map(|data| {
|
||||||
|
data.borrow()
|
||||||
|
.contains_point(&*states.cached_state.current(), point - location.to_f64())
|
||||||
|
})
|
||||||
|
.unwrap_or(false);
|
||||||
|
if contains_the_point {
|
||||||
|
*found.borrow_mut() = Some((wl_surface.clone(), location));
|
||||||
|
}
|
||||||
|
|
||||||
|
TraversalAction::DoChildren(location)
|
||||||
|
},
|
||||||
|
|_, _, _| {},
|
||||||
|
|_, _, _| {
|
||||||
|
// only continue if the point is not found
|
||||||
|
found.borrow().is_none()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
found.into_inner()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn self_update(&mut self) {
|
||||||
|
let mut bounding_box = Rectangle::from_loc_and_size(self.location, (0, 0));
|
||||||
|
if let Some(wl_surface) = self.surface.get_surface() {
|
||||||
|
with_surface_tree_downward(
|
||||||
|
wl_surface,
|
||||||
|
self.location,
|
||||||
|
|_, states, &loc| {
|
||||||
|
let mut loc = loc;
|
||||||
|
let data = states.data_map.get::<RefCell<SurfaceData>>();
|
||||||
|
|
||||||
|
if let Some(size) = data.and_then(|d| d.borrow().size()) {
|
||||||
|
if states.role == Some("subsurface") {
|
||||||
|
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
||||||
|
loc += current.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the bounding box.
|
||||||
|
bounding_box = bounding_box.merge(Rectangle::from_loc_and_size(loc, size));
|
||||||
|
|
||||||
|
TraversalAction::DoChildren(loc)
|
||||||
|
} else {
|
||||||
|
// If the parent surface is unmapped, then the child surfaces are hidden as
|
||||||
|
// well, no need to consider them here.
|
||||||
|
TraversalAction::SkipChildren
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|_, _, _| {},
|
||||||
|
|_, _, _| true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.bbox = bounding_box;
|
||||||
|
|
||||||
|
if let Some(surface) = self.surface.get_surface() {
|
||||||
|
self.layer = with_states(surface, |states| {
|
||||||
|
let current = states.cached_state.current::<LayerSurfaceCachedState>();
|
||||||
|
current.layer
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends the frame callback to all the subsurfaces in this
|
||||||
|
/// window that requested it
|
||||||
|
fn send_frame(&self, time: u32) {
|
||||||
|
if let Some(wl_surface) = self.surface.get_surface() {
|
||||||
|
with_surface_tree_downward(
|
||||||
|
wl_surface,
|
||||||
|
(),
|
||||||
|
|_, _, &()| TraversalAction::DoChildren(()),
|
||||||
|
|_, states, &()| {
|
||||||
|
// the surface may not have any user_data if it is a subsurface and has not
|
||||||
|
// yet been commited
|
||||||
|
SurfaceData::send_frame(&mut *states.cached_state.current(), time)
|
||||||
|
},
|
||||||
|
|_, _, &()| true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct LayerMap {
|
||||||
|
surfaces: Vec<LayerSurface>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayerMap {
|
||||||
|
pub fn insert(&mut self, surface: wlr_layer::LayerSurface, layer: wlr_layer::Layer) {
|
||||||
|
let mut layer = LayerSurface {
|
||||||
|
location: Default::default(),
|
||||||
|
bbox: Rectangle::default(),
|
||||||
|
surface,
|
||||||
|
layer,
|
||||||
|
};
|
||||||
|
layer.self_update();
|
||||||
|
self.surfaces.insert(0, layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_surface_under(
|
||||||
|
&self,
|
||||||
|
layer: &wlr_layer::Layer,
|
||||||
|
point: Point<f64, Logical>,
|
||||||
|
) -> Option<(wl_surface::WlSurface, Point<i32, Logical>)> {
|
||||||
|
for l in self.surfaces.iter().filter(|s| &s.layer == layer) {
|
||||||
|
if let Some(surface) = l.matching(point) {
|
||||||
|
return Some(surface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_layers_from_bottom_to_top<Func>(&self, layer: &wlr_layer::Layer, mut f: Func)
|
||||||
|
where
|
||||||
|
Func: FnMut(&LayerSurface),
|
||||||
|
{
|
||||||
|
for l in self.surfaces.iter().filter(|s| &s.layer == layer).rev() {
|
||||||
|
f(l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh(&mut self) {
|
||||||
|
self.surfaces.retain(|l| l.surface.alive());
|
||||||
|
|
||||||
|
for l in self.surfaces.iter_mut() {
|
||||||
|
l.self_update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the layer corresponding to the given `WlSurface`.
|
||||||
|
pub fn find(&self, surface: &wl_surface::WlSurface) -> Option<&LayerSurface> {
|
||||||
|
self.surfaces.iter().find_map(|l| {
|
||||||
|
if l.surface
|
||||||
|
.get_surface()
|
||||||
|
.map(|s| s.as_ref().equals(surface.as_ref()))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
Some(l)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn arange_layers(&mut self, output: &Output) {
|
||||||
|
let output_rect = output.geometry();
|
||||||
|
|
||||||
|
// Get all layer surfaces assigned to this output
|
||||||
|
let surfaces: Vec<_> = output
|
||||||
|
.layer_surfaces()
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.as_ref().clone())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Find layers for this output
|
||||||
|
let filtered_layers = self.surfaces.iter_mut().filter(|l| {
|
||||||
|
l.surface
|
||||||
|
.get_surface()
|
||||||
|
.map(|s| surfaces.contains(s.as_ref()))
|
||||||
|
.unwrap_or(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
for layer in filtered_layers {
|
||||||
|
let surface = if let Some(surface) = layer.surface.get_surface() {
|
||||||
|
surface
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = with_states(surface, |states| {
|
||||||
|
*states.cached_state.current::<LayerSurfaceCachedState>()
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let x = if data.size.w == 0 || data.anchor.contains(Anchor::LEFT) {
|
||||||
|
output_rect.loc.x
|
||||||
|
} else if data.anchor.contains(Anchor::RIGHT) {
|
||||||
|
output_rect.loc.x + (output_rect.size.w - data.size.w)
|
||||||
|
} else {
|
||||||
|
output_rect.loc.x + ((output_rect.size.w / 2) - (data.size.w / 2))
|
||||||
|
};
|
||||||
|
|
||||||
|
let y = if data.size.h == 0 || data.anchor.contains(Anchor::TOP) {
|
||||||
|
output_rect.loc.y
|
||||||
|
} else if data.anchor.contains(Anchor::BOTTOM) {
|
||||||
|
output_rect.loc.y + (output_rect.size.h - data.size.h)
|
||||||
|
} else {
|
||||||
|
output_rect.loc.y + ((output_rect.size.h / 2) - (data.size.h / 2))
|
||||||
|
};
|
||||||
|
|
||||||
|
let location: Point<i32, Logical> = (x, y).into();
|
||||||
|
|
||||||
|
layer
|
||||||
|
.surface
|
||||||
|
.with_pending_state(|state| {
|
||||||
|
state.size = Some(output_rect.size);
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
layer.surface.send_configure();
|
||||||
|
|
||||||
|
layer.location = location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_frames(&self, time: u32) {
|
||||||
|
for layer in &self.surfaces {
|
||||||
|
layer.send_frame(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ use smithay::{
|
||||||
wayland::{
|
wayland::{
|
||||||
output::{Mode, PhysicalProperties},
|
output::{Mode, PhysicalProperties},
|
||||||
seat::CursorImageStatus,
|
seat::CursorImageStatus,
|
||||||
|
shell::wlr_layer::Layer,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -144,15 +145,34 @@ pub fn run_winit(log: Logger) {
|
||||||
.render(|renderer, frame| {
|
.render(|renderer, frame| {
|
||||||
frame.clear([0.8, 0.8, 0.9, 1.0])?;
|
frame.clear([0.8, 0.8, 0.9, 1.0])?;
|
||||||
|
|
||||||
|
let window_map = &*state.window_map.borrow();
|
||||||
|
|
||||||
|
for layer in [Layer::Background, Layer::Bottom] {
|
||||||
|
draw_layers(
|
||||||
|
renderer,
|
||||||
|
frame,
|
||||||
|
window_map,
|
||||||
|
layer,
|
||||||
|
output_geometry,
|
||||||
|
output_scale,
|
||||||
|
&log,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
// draw the windows
|
// draw the windows
|
||||||
draw_windows(
|
draw_windows(renderer, frame, window_map, output_geometry, output_scale, &log)?;
|
||||||
renderer,
|
|
||||||
frame,
|
for layer in [Layer::Top, Layer::Overlay] {
|
||||||
&*state.window_map.borrow(),
|
draw_layers(
|
||||||
output_geometry,
|
renderer,
|
||||||
output_scale,
|
frame,
|
||||||
&log,
|
window_map,
|
||||||
)?;
|
layer,
|
||||||
|
output_geometry,
|
||||||
|
output_scale,
|
||||||
|
&log,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
let (x, y) = state.pointer_location.into();
|
let (x, y) = state.pointer_location.into();
|
||||||
// draw the dnd icon if any
|
// draw the dnd icon if any
|
||||||
|
|
|
@ -22,6 +22,8 @@ use wayland_server::protocol::wl_surface::WlSurface;
|
||||||
pub mod legacy;
|
pub mod legacy;
|
||||||
pub mod xdg;
|
pub mod xdg;
|
||||||
|
|
||||||
|
pub mod wlr_layer;
|
||||||
|
|
||||||
/// Represents the possible errors returned from
|
/// Represents the possible errors returned from
|
||||||
/// a surface ping
|
/// a surface ping
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
|
@ -0,0 +1,298 @@
|
||||||
|
use std::{convert::TryFrom, ops::Deref, sync::Mutex};
|
||||||
|
|
||||||
|
use wayland_protocols::wlr::unstable::layer_shell::v1::server::{zwlr_layer_shell_v1, zwlr_layer_surface_v1};
|
||||||
|
use wayland_server::{protocol::wl_surface, DispatchData, Filter, Main};
|
||||||
|
|
||||||
|
use crate::wayland::{compositor, shell::wlr_layer::Layer, Serial};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
Anchor, KeyboardInteractivity, LayerShellRequest, LayerSurfaceAttributes, LayerSurfaceCachedState,
|
||||||
|
Margins, ShellUserData,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::LAYER_SURFACE_ROLE;
|
||||||
|
|
||||||
|
pub(super) fn layer_shell_implementation(
|
||||||
|
shell: Main<zwlr_layer_shell_v1::ZwlrLayerShellV1>,
|
||||||
|
request: zwlr_layer_shell_v1::Request,
|
||||||
|
dispatch_data: DispatchData<'_>,
|
||||||
|
) {
|
||||||
|
let data = shell.as_ref().user_data().get::<ShellUserData>().unwrap();
|
||||||
|
match request {
|
||||||
|
zwlr_layer_shell_v1::Request::GetLayerSurface {
|
||||||
|
id,
|
||||||
|
surface,
|
||||||
|
output,
|
||||||
|
layer,
|
||||||
|
namespace,
|
||||||
|
} => {
|
||||||
|
let layer = match Layer::try_from(layer) {
|
||||||
|
Ok(layer) => layer,
|
||||||
|
Err((err, msg)) => {
|
||||||
|
id.as_ref().post_error(err as u32, msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if compositor::give_role(&surface, LAYER_SURFACE_ROLE).is_err() {
|
||||||
|
shell.as_ref().post_error(
|
||||||
|
zwlr_layer_shell_v1::Error::Role as u32,
|
||||||
|
"Surface already has a role.".into(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
compositor::with_states(&surface, |states| {
|
||||||
|
states.data_map.insert_if_missing_threadsafe(|| {
|
||||||
|
Mutex::new(LayerSurfaceAttributes::new(id.deref().clone()))
|
||||||
|
});
|
||||||
|
|
||||||
|
states.cached_state.pending::<LayerSurfaceCachedState>().layer = layer;
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
compositor::add_commit_hook(&surface, |surface| {
|
||||||
|
compositor::with_states(surface, |states| {
|
||||||
|
let mut guard = states
|
||||||
|
.data_map
|
||||||
|
.get::<Mutex<LayerSurfaceAttributes>>()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let pending = states.cached_state.pending::<LayerSurfaceCachedState>();
|
||||||
|
|
||||||
|
if pending.size.w == 0 && !pending.anchor.anchored_horizontally() {
|
||||||
|
guard.surface.as_ref().post_error(
|
||||||
|
zwlr_layer_surface_v1::Error::InvalidSize as u32,
|
||||||
|
"width 0 requested without setting left and right anchors".into(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if pending.size.h == 0 && !pending.anchor.anchored_vertically() {
|
||||||
|
guard.surface.as_ref().post_error(
|
||||||
|
zwlr_layer_surface_v1::Error::InvalidSize as u32,
|
||||||
|
"height 0 requested without setting top and bottom anchors".into(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(state) = guard.last_acked.clone() {
|
||||||
|
guard.current = state;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
id.quick_assign(|surface, req, dispatch_data| {
|
||||||
|
layer_surface_implementation(surface.deref().clone(), req, dispatch_data)
|
||||||
|
});
|
||||||
|
|
||||||
|
id.assign_destructor(Filter::new(
|
||||||
|
|layer_surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, _, _| {
|
||||||
|
let data = layer_surface
|
||||||
|
.as_ref()
|
||||||
|
.user_data()
|
||||||
|
.get::<LayerSurfaceUserData>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// remove this surface from the known ones (as well as any leftover dead surface)
|
||||||
|
data.shell_data
|
||||||
|
.shell_state
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.known_layers
|
||||||
|
.retain(|other| other.alive());
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
id.as_ref().user_data().set(|| LayerSurfaceUserData {
|
||||||
|
shell_data: data.clone(),
|
||||||
|
wl_surface: surface.clone(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let handle = super::LayerSurface {
|
||||||
|
wl_surface: surface,
|
||||||
|
shell_surface: id.deref().clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
data.shell_state.lock().unwrap().known_layers.push(handle.clone());
|
||||||
|
|
||||||
|
let mut user_impl = data.user_impl.borrow_mut();
|
||||||
|
(&mut *user_impl)(
|
||||||
|
LayerShellRequest::NewLayerSurface {
|
||||||
|
surface: handle,
|
||||||
|
output,
|
||||||
|
layer,
|
||||||
|
namespace,
|
||||||
|
},
|
||||||
|
dispatch_data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
zwlr_layer_shell_v1::Request::Destroy => {
|
||||||
|
// Handled by destructor
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layer_surface_implementation(
|
||||||
|
layer_surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
|
||||||
|
request: zwlr_layer_surface_v1::Request,
|
||||||
|
dispatch_data: DispatchData<'_>,
|
||||||
|
) {
|
||||||
|
match request {
|
||||||
|
zwlr_layer_surface_v1::Request::SetSize { width, height } => {
|
||||||
|
with_surface_pending_state(&layer_surface, |data| {
|
||||||
|
data.size = (width as i32, height as i32).into();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
zwlr_layer_surface_v1::Request::SetAnchor { anchor } => {
|
||||||
|
match Anchor::try_from(anchor) {
|
||||||
|
Ok(anchor) => {
|
||||||
|
with_surface_pending_state(&layer_surface, |data| {
|
||||||
|
data.anchor = Anchor::from_bits(anchor.bits()).unwrap_or_default();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err((err, msg)) => {
|
||||||
|
layer_surface.as_ref().post_error(err as u32, msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
zwlr_layer_surface_v1::Request::SetExclusiveZone { zone } => {
|
||||||
|
with_surface_pending_state(&layer_surface, |data| {
|
||||||
|
data.exclusive_zone = zone.into();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
zwlr_layer_surface_v1::Request::SetMargin {
|
||||||
|
top,
|
||||||
|
right,
|
||||||
|
bottom,
|
||||||
|
left,
|
||||||
|
} => {
|
||||||
|
with_surface_pending_state(&layer_surface, |data| {
|
||||||
|
data.margin = Margins {
|
||||||
|
top,
|
||||||
|
right,
|
||||||
|
bottom,
|
||||||
|
left,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
zwlr_layer_surface_v1::Request::SetKeyboardInteractivity {
|
||||||
|
keyboard_interactivity,
|
||||||
|
} => {
|
||||||
|
match KeyboardInteractivity::try_from(keyboard_interactivity) {
|
||||||
|
Ok(keyboard_interactivity) => {
|
||||||
|
with_surface_pending_state(&layer_surface, |data| {
|
||||||
|
data.keyboard_interactivity = keyboard_interactivity;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err((err, msg)) => {
|
||||||
|
layer_surface.as_ref().post_error(err as u32, msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
zwlr_layer_surface_v1::Request::SetLayer { layer } => {
|
||||||
|
match Layer::try_from(layer) {
|
||||||
|
Ok(layer) => {
|
||||||
|
with_surface_pending_state(&layer_surface, |data| {
|
||||||
|
data.layer = layer;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err((err, msg)) => {
|
||||||
|
layer_surface.as_ref().post_error(err as u32, msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
zwlr_layer_surface_v1::Request::GetPopup { popup } => {
|
||||||
|
let data = layer_surface
|
||||||
|
.as_ref()
|
||||||
|
.user_data()
|
||||||
|
.get::<LayerSurfaceUserData>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let parent_surface = data.wl_surface.clone();
|
||||||
|
|
||||||
|
let data = popup
|
||||||
|
.as_ref()
|
||||||
|
.user_data()
|
||||||
|
.get::<crate::wayland::shell::xdg::xdg_handlers::ShellSurfaceUserData>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
compositor::with_states(&data.wl_surface, move |states| {
|
||||||
|
states
|
||||||
|
.data_map
|
||||||
|
.get::<Mutex<crate::wayland::shell::xdg::XdgPopupSurfaceRoleAttributes>>()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.parent = Some(parent_surface);
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
zwlr_layer_surface_v1::Request::AckConfigure { serial } => {
|
||||||
|
let data = layer_surface
|
||||||
|
.as_ref()
|
||||||
|
.user_data()
|
||||||
|
.get::<LayerSurfaceUserData>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let serial = Serial::from(serial);
|
||||||
|
let surface = &data.wl_surface;
|
||||||
|
|
||||||
|
let found_configure = compositor::with_states(surface, |states| {
|
||||||
|
states
|
||||||
|
.data_map
|
||||||
|
.get::<Mutex<LayerSurfaceAttributes>>()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.ack_configure(serial)
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let configure = match found_configure {
|
||||||
|
Some(configure) => configure,
|
||||||
|
None => {
|
||||||
|
layer_surface.as_ref().post_error(
|
||||||
|
zwlr_layer_surface_v1::Error::InvalidSurfaceState as u32,
|
||||||
|
format!("wrong configure serial: {}", <u32>::from(serial)),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut user_impl = data.shell_data.user_impl.borrow_mut();
|
||||||
|
(&mut *user_impl)(
|
||||||
|
LayerShellRequest::AckConfigure {
|
||||||
|
surface: data.wl_surface.clone(),
|
||||||
|
configure,
|
||||||
|
},
|
||||||
|
dispatch_data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LayerSurfaceUserData {
|
||||||
|
shell_data: ShellUserData,
|
||||||
|
wl_surface: wl_surface::WlSurface,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_surface_pending_state<F, T>(layer_surface: &zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut LayerSurfaceCachedState) -> T,
|
||||||
|
{
|
||||||
|
let data = layer_surface
|
||||||
|
.as_ref()
|
||||||
|
.user_data()
|
||||||
|
.get::<LayerSurfaceUserData>()
|
||||||
|
.unwrap();
|
||||||
|
compositor::with_states(&data.wl_surface, |states| {
|
||||||
|
f(&mut *states.cached_state.pending::<LayerSurfaceCachedState>())
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
}
|
|
@ -0,0 +1,454 @@
|
||||||
|
//! Utilities for handling shell surfaces with the `wlr_layer_shell` protocol
|
||||||
|
//!
|
||||||
|
//! This interface should be suitable for the implementation of many desktop shell components,
|
||||||
|
//! and a broad number of other applications that interact with the desktop.
|
||||||
|
//!
|
||||||
|
//! ### Initialization
|
||||||
|
//!
|
||||||
|
//! To initialize this handler, simple use the [`wlr_layer_shell_init`] function provided in this module.
|
||||||
|
//! You need to provide a closure that will be invoked whenever some action is required from you,
|
||||||
|
//! are represented by the [`LayerShellRequest`] enum.
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! # extern crate wayland_server;
|
||||||
|
//! #
|
||||||
|
//! use smithay::wayland::shell::wlr_layer::{wlr_layer_shell_init, LayerShellRequest};
|
||||||
|
//!
|
||||||
|
//! # let mut display = wayland_server::Display::new();
|
||||||
|
//! let (shell_state, _) = wlr_layer_shell_init(
|
||||||
|
//! &mut display,
|
||||||
|
//! // your implementation
|
||||||
|
//! |event: LayerShellRequest, dispatch_data| { /* handle the shell requests here */ },
|
||||||
|
//! None // put a logger if you want
|
||||||
|
//! );
|
||||||
|
//!
|
||||||
|
//! // You're now ready to go!
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
rc::Rc,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
|
use wayland_protocols::wlr::unstable::layer_shell::v1::server::{zwlr_layer_shell_v1, zwlr_layer_surface_v1};
|
||||||
|
use wayland_server::{
|
||||||
|
protocol::{wl_output::WlOutput, wl_surface},
|
||||||
|
DispatchData, Display, Filter, Global, Main,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
utils::{DeadResource, Logical, Size},
|
||||||
|
wayland::{
|
||||||
|
compositor::{self, Cacheable},
|
||||||
|
Serial, SERIAL_COUNTER,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mod handlers;
|
||||||
|
mod types;
|
||||||
|
|
||||||
|
pub use types::{Anchor, ExclusiveZone, KeyboardInteractivity, Layer, Margins};
|
||||||
|
|
||||||
|
/// The role of a wlr_layer_shell_surface
|
||||||
|
pub const LAYER_SURFACE_ROLE: &str = "zwlr_layer_surface_v1";
|
||||||
|
|
||||||
|
/// Attributes for layer surface
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LayerSurfaceAttributes {
|
||||||
|
surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
|
||||||
|
/// Defines if the surface has received at least one
|
||||||
|
/// layer_surface.ack_configure from the client
|
||||||
|
pub configured: bool,
|
||||||
|
/// The serial of the last acked configure
|
||||||
|
pub configure_serial: Option<Serial>,
|
||||||
|
/// Holds the state if the surface has sent the initial
|
||||||
|
/// configure event to the client. It is expected that
|
||||||
|
/// during the first commit a initial
|
||||||
|
/// configure event is sent to the client
|
||||||
|
pub initial_configure_sent: bool,
|
||||||
|
/// Holds the configures the server has sent out
|
||||||
|
/// to the client waiting to be acknowledged by
|
||||||
|
/// the client. All pending configures that are older
|
||||||
|
/// than the acknowledged one will be discarded during
|
||||||
|
/// processing layer_surface.ack_configure.
|
||||||
|
pending_configures: Vec<LayerSurfaceConfigure>,
|
||||||
|
/// Holds the pending state as set by the server.
|
||||||
|
pub server_pending: Option<LayerSurfaceState>,
|
||||||
|
/// Holds the last server_pending state that has been acknowledged
|
||||||
|
/// by the client. This state should be cloned to the current
|
||||||
|
/// during a commit.
|
||||||
|
pub last_acked: Option<LayerSurfaceState>,
|
||||||
|
/// Holds the current state of the layer after a successful
|
||||||
|
/// commit.
|
||||||
|
pub current: LayerSurfaceState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayerSurfaceAttributes {
|
||||||
|
fn new(surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1) -> Self {
|
||||||
|
Self {
|
||||||
|
surface,
|
||||||
|
configured: false,
|
||||||
|
configure_serial: None,
|
||||||
|
initial_configure_sent: false,
|
||||||
|
pending_configures: Vec::new(),
|
||||||
|
server_pending: None,
|
||||||
|
last_acked: None,
|
||||||
|
current: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ack_configure(&mut self, serial: Serial) -> Option<LayerSurfaceConfigure> {
|
||||||
|
let configure = self
|
||||||
|
.pending_configures
|
||||||
|
.iter()
|
||||||
|
.find(|configure| configure.serial == serial)
|
||||||
|
.cloned()?;
|
||||||
|
|
||||||
|
self.last_acked = Some(configure.state.clone());
|
||||||
|
|
||||||
|
self.configured = true;
|
||||||
|
self.configure_serial = Some(serial);
|
||||||
|
self.pending_configures.retain(|c| c.serial > serial);
|
||||||
|
Some(configure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// State of a layer surface
|
||||||
|
#[derive(Debug, Default, Clone, PartialEq)]
|
||||||
|
pub struct LayerSurfaceState {
|
||||||
|
/// The suggested size of the surface
|
||||||
|
pub size: Option<Size<i32, Logical>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the client pending state
|
||||||
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
|
pub struct LayerSurfaceCachedState {
|
||||||
|
/// The size requested by the client
|
||||||
|
pub size: Size<i32, Logical>,
|
||||||
|
/// Anchor bitflags, describing how the layers surface should be positioned and sized
|
||||||
|
pub anchor: Anchor,
|
||||||
|
/// Descripton of exclusive zone
|
||||||
|
pub exclusive_zone: ExclusiveZone,
|
||||||
|
/// Describes distance from the anchor point of the output
|
||||||
|
pub margin: Margins,
|
||||||
|
/// Describes how keyboard events are delivered to this surface
|
||||||
|
pub keyboard_interactivity: KeyboardInteractivity,
|
||||||
|
/// The layer that the surface is rendered on
|
||||||
|
pub layer: Layer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cacheable for LayerSurfaceCachedState {
|
||||||
|
fn commit(&mut self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
fn merge_into(self, into: &mut Self) {
|
||||||
|
*into = self;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shell global state
|
||||||
|
///
|
||||||
|
/// This state allows you to retrieve a list of surfaces
|
||||||
|
/// currently known to the shell global.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LayerShellState {
|
||||||
|
known_layers: Vec<LayerSurface>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayerShellState {
|
||||||
|
/// Access all the shell surfaces known by this handler
|
||||||
|
pub fn layer_surfaces(&self) -> &[LayerSurface] {
|
||||||
|
&self.known_layers[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct ShellUserData {
|
||||||
|
log: ::slog::Logger,
|
||||||
|
user_impl: Rc<RefCell<dyn FnMut(LayerShellRequest, DispatchData<'_>)>>,
|
||||||
|
shell_state: Arc<Mutex<LayerShellState>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `wlr_layer_shell` globals
|
||||||
|
pub fn wlr_layer_shell_init<L, Impl>(
|
||||||
|
display: &mut Display,
|
||||||
|
implementation: Impl,
|
||||||
|
logger: L,
|
||||||
|
) -> (
|
||||||
|
Arc<Mutex<LayerShellState>>,
|
||||||
|
Global<zwlr_layer_shell_v1::ZwlrLayerShellV1>,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
L: Into<Option<::slog::Logger>>,
|
||||||
|
Impl: FnMut(LayerShellRequest, DispatchData<'_>) + 'static,
|
||||||
|
{
|
||||||
|
let log = crate::slog_or_fallback(logger);
|
||||||
|
let shell_state = Arc::new(Mutex::new(LayerShellState {
|
||||||
|
known_layers: Vec::new(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
let shell_data = ShellUserData {
|
||||||
|
log: log.new(slog::o!("smithay_module" => "layer_shell_handler")),
|
||||||
|
user_impl: Rc::new(RefCell::new(implementation)),
|
||||||
|
shell_state: shell_state.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let layer_shell_global = display.create_global(
|
||||||
|
4,
|
||||||
|
Filter::new(
|
||||||
|
move |(shell, _version): (Main<zwlr_layer_shell_v1::ZwlrLayerShellV1>, _), _, _ddata| {
|
||||||
|
shell.quick_assign(self::handlers::layer_shell_implementation);
|
||||||
|
shell.as_ref().user_data().set({
|
||||||
|
let shell_data = shell_data.clone();
|
||||||
|
move || shell_data
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
(shell_state, layer_shell_global)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A handle to a layer surface
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LayerSurface {
|
||||||
|
wl_surface: wl_surface::WlSurface,
|
||||||
|
shell_surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::cmp::PartialEq for LayerSurface {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.alive() && other.alive() && self.wl_surface == other.wl_surface
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayerSurface {
|
||||||
|
/// Is the layer surface referred by this handle still alive?
|
||||||
|
pub fn alive(&self) -> bool {
|
||||||
|
self.shell_surface.as_ref().is_alive() && self.wl_surface.as_ref().is_alive()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the current pending state for a configure
|
||||||
|
///
|
||||||
|
/// Returns `Some` if either no initial configure has been sent or
|
||||||
|
/// the `server_pending` is `Some` and different from the last pending
|
||||||
|
/// configure or `last_acked` if there is no pending
|
||||||
|
///
|
||||||
|
/// Returns `None` if either no `server_pending` or the pending
|
||||||
|
/// has already been sent to the client or the pending is equal
|
||||||
|
/// to the `last_acked`
|
||||||
|
fn get_pending_state(&self, attributes: &mut LayerSurfaceAttributes) -> Option<LayerSurfaceState> {
|
||||||
|
if !attributes.initial_configure_sent {
|
||||||
|
return Some(attributes.server_pending.take().unwrap_or_default());
|
||||||
|
}
|
||||||
|
|
||||||
|
let server_pending = match attributes.server_pending.take() {
|
||||||
|
Some(state) => state,
|
||||||
|
None => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let last_state = attributes
|
||||||
|
.pending_configures
|
||||||
|
.last()
|
||||||
|
.map(|c| &c.state)
|
||||||
|
.or_else(|| attributes.last_acked.as_ref());
|
||||||
|
|
||||||
|
if let Some(state) = last_state {
|
||||||
|
if state == &server_pending {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(server_pending)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a configure event to this layer surface to suggest it a new configuration
|
||||||
|
///
|
||||||
|
/// The serial of this configure will be tracked waiting for the client to ACK it.
|
||||||
|
///
|
||||||
|
/// You can manipulate the state that will be sent to the client with the [`with_pending_state`](#method.with_pending_state)
|
||||||
|
/// method.
|
||||||
|
pub fn send_configure(&self) {
|
||||||
|
if let Some(surface) = self.get_surface() {
|
||||||
|
let configure = compositor::with_states(surface, |states| {
|
||||||
|
let mut attributes = states
|
||||||
|
.data_map
|
||||||
|
.get::<Mutex<LayerSurfaceAttributes>>()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap();
|
||||||
|
if let Some(pending) = self.get_pending_state(&mut *attributes) {
|
||||||
|
let configure = LayerSurfaceConfigure {
|
||||||
|
serial: SERIAL_COUNTER.next_serial(),
|
||||||
|
state: pending,
|
||||||
|
};
|
||||||
|
|
||||||
|
attributes.pending_configures.push(configure.clone());
|
||||||
|
attributes.initial_configure_sent = true;
|
||||||
|
|
||||||
|
Some(configure)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(None);
|
||||||
|
|
||||||
|
// send surface configure
|
||||||
|
if let Some(configure) = configure {
|
||||||
|
let (width, height) = configure.state.size.unwrap_or_default().into();
|
||||||
|
let serial = configure.serial;
|
||||||
|
self.shell_surface
|
||||||
|
.configure(serial.into(), width as u32, height as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make sure this surface was configured
|
||||||
|
///
|
||||||
|
/// Returns `true` if it was, if not, returns `false` and raise
|
||||||
|
/// a protocol error to the associated layer surface. Also returns `false`
|
||||||
|
/// if the surface is already destroyed.
|
||||||
|
pub fn ensure_configured(&self) -> bool {
|
||||||
|
if !self.alive() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let configured = compositor::with_states(&self.wl_surface, |states| {
|
||||||
|
states
|
||||||
|
.data_map
|
||||||
|
.get::<Mutex<LayerSurfaceAttributes>>()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.configured
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
if !configured {
|
||||||
|
self.shell_surface.as_ref().post_error(
|
||||||
|
zwlr_layer_shell_v1::Error::AlreadyConstructed as u32,
|
||||||
|
"layer_surface has never been configured".into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
configured
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a "close" event to the client
|
||||||
|
pub fn send_close(&self) {
|
||||||
|
self.shell_surface.closed()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the underlying `wl_surface` of this layer surface
|
||||||
|
///
|
||||||
|
/// Returns `None` if the layer surface actually no longer exists.
|
||||||
|
pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> {
|
||||||
|
if self.alive() {
|
||||||
|
Some(&self.wl_surface)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows the pending state of this layer to
|
||||||
|
/// be manipulated.
|
||||||
|
///
|
||||||
|
/// This should be used to inform the client about size and state changes,
|
||||||
|
/// for example after a resize request from the client.
|
||||||
|
///
|
||||||
|
/// The state will be sent to the client when calling [`send_configure`](#method.send_configure).
|
||||||
|
pub fn with_pending_state<F, T>(&self, f: F) -> Result<T, DeadResource>
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut LayerSurfaceState) -> T,
|
||||||
|
{
|
||||||
|
if !self.alive() {
|
||||||
|
return Err(DeadResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(compositor::with_states(&self.wl_surface, |states| {
|
||||||
|
let mut attributes = states
|
||||||
|
.data_map
|
||||||
|
.get::<Mutex<LayerSurfaceAttributes>>()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap();
|
||||||
|
if attributes.server_pending.is_none() {
|
||||||
|
attributes.server_pending = Some(attributes.current.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let server_pending = attributes.server_pending.as_mut().unwrap();
|
||||||
|
f(server_pending)
|
||||||
|
})
|
||||||
|
.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a copy of the current state of this layer
|
||||||
|
///
|
||||||
|
/// Returns `None` if the underlying surface has been
|
||||||
|
/// destroyed
|
||||||
|
pub fn current_state(&self) -> Option<LayerSurfaceState> {
|
||||||
|
if !self.alive() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(
|
||||||
|
compositor::with_states(&self.wl_surface, |states| {
|
||||||
|
let attributes = states
|
||||||
|
.data_map
|
||||||
|
.get::<Mutex<LayerSurfaceAttributes>>()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
attributes.current.clone()
|
||||||
|
})
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A configure message for layer surfaces
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LayerSurfaceConfigure {
|
||||||
|
/// The state associated with this configure
|
||||||
|
pub state: LayerSurfaceState,
|
||||||
|
|
||||||
|
/// A serial number to track ACK from the client
|
||||||
|
///
|
||||||
|
/// This should be an ever increasing number, as the ACK-ing
|
||||||
|
/// from a client for a serial will validate all pending lower
|
||||||
|
/// serials.
|
||||||
|
pub serial: Serial,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Events generated by layer shell surfaces
|
||||||
|
///
|
||||||
|
/// Depending on what you want to do, you might ignore some of them
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum LayerShellRequest {
|
||||||
|
/// A new layer surface was created
|
||||||
|
///
|
||||||
|
/// You likely need to send a [`LayerSurfaceConfigure`] to the surface, to hint the
|
||||||
|
/// client as to how its layer surface should be sized.
|
||||||
|
NewLayerSurface {
|
||||||
|
/// the surface
|
||||||
|
surface: LayerSurface,
|
||||||
|
/// The output that the layer will be displayed on
|
||||||
|
///
|
||||||
|
/// None means that the compositor should decide which output to use,
|
||||||
|
/// Generally this will be the one that the user most recently interacted with
|
||||||
|
output: Option<WlOutput>,
|
||||||
|
/// This values indicate on which layer a surface should be rendered on
|
||||||
|
layer: Layer,
|
||||||
|
/// namespace that defines the purpose of the layer surface
|
||||||
|
namespace: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A surface has acknowledged a configure serial.
|
||||||
|
AckConfigure {
|
||||||
|
/// The surface.
|
||||||
|
surface: wl_surface::WlSurface,
|
||||||
|
/// The configure serial.
|
||||||
|
configure: LayerSurfaceConfigure,
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,231 @@
|
||||||
|
use std::{cmp::Ordering, convert::TryFrom};
|
||||||
|
|
||||||
|
use wayland_protocols::wlr::unstable::layer_shell::v1::server::{zwlr_layer_shell_v1, zwlr_layer_surface_v1};
|
||||||
|
|
||||||
|
/// Available layers for surfaces
|
||||||
|
///
|
||||||
|
/// These values indicate which layers a surface can be rendered in.
|
||||||
|
/// They are ordered by z depth, bottom-most first.
|
||||||
|
/// Traditional shell surfaces will typically be rendered between the bottom and top layers.
|
||||||
|
/// Fullscreen shell surfaces are typically rendered at the top layer.
|
||||||
|
/// Multiple surfaces can share a single layer, and ordering within a single layer is undefined.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum Layer {
|
||||||
|
/// The lowest layer, used usualy for wallpapers
|
||||||
|
Background,
|
||||||
|
/// The layer bellow the windows and above the wallpaper
|
||||||
|
Bottom,
|
||||||
|
/// The layer above the windows and bellow overlay
|
||||||
|
Top,
|
||||||
|
/// The top layer above all other layers
|
||||||
|
Overlay,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<zwlr_layer_shell_v1::Layer> for Layer {
|
||||||
|
type Error = (zwlr_layer_shell_v1::Error, String);
|
||||||
|
|
||||||
|
fn try_from(layer: zwlr_layer_shell_v1::Layer) -> Result<Self, Self::Error> {
|
||||||
|
match layer {
|
||||||
|
zwlr_layer_shell_v1::Layer::Background => Ok(Self::Background),
|
||||||
|
zwlr_layer_shell_v1::Layer::Bottom => Ok(Self::Bottom),
|
||||||
|
zwlr_layer_shell_v1::Layer::Top => Ok(Self::Top),
|
||||||
|
zwlr_layer_shell_v1::Layer::Overlay => Ok(Self::Overlay),
|
||||||
|
_ => Err((
|
||||||
|
zwlr_layer_shell_v1::Error::InvalidLayer,
|
||||||
|
format!("invalid layer: {:?}", layer),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Layer {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Background
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Types of keyboard interaction possible for a layer shell surface
|
||||||
|
///
|
||||||
|
/// The rationale for this is twofold:
|
||||||
|
/// - some applications are not interested in keyboard events
|
||||||
|
/// and not allowing them to be focused can improve the desktop experience
|
||||||
|
/// - some applications will want to take exclusive keyboard focus.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum KeyboardInteractivity {
|
||||||
|
/// This value indicates that this surface is not interested in keyboard events
|
||||||
|
/// and the compositor should never assign it the keyboard focus.
|
||||||
|
///
|
||||||
|
/// This is the default value, set for newly created layer shell surfaces.
|
||||||
|
///
|
||||||
|
/// This is useful for e.g. desktop widgets that display information
|
||||||
|
/// or only have interaction with non-keyboard input devices.
|
||||||
|
None,
|
||||||
|
/// Request exclusive keyboard focus if this surface is above the shell surface layer.
|
||||||
|
///
|
||||||
|
/// For the top and overlay layers, the seat will always give exclusive keyboard focus
|
||||||
|
/// to the top-most layer which has keyboard interactivity set to exclusive.
|
||||||
|
/// If this layer contains multiple surfaces with keyboard interactivity set to exclusive,
|
||||||
|
/// the compositor determines the one receiving keyboard events in an implementation- defined manner.
|
||||||
|
/// In this case, no guarantee is made when this surface will receive keyboard focus (if ever).
|
||||||
|
///
|
||||||
|
/// For the bottom and background layers, the compositor is allowed to use normal focus semantics.
|
||||||
|
///
|
||||||
|
/// This setting is mainly intended for applications that need to
|
||||||
|
/// ensure they receive all keyboard events, such as a lock screen or a password prompt.
|
||||||
|
Exclusive,
|
||||||
|
/// This requests the compositor to allow this surface
|
||||||
|
/// to be focused and unfocused by the user in an implementation-defined manner.
|
||||||
|
/// The user should be able to unfocus this surface even regardless of the layer it is on.
|
||||||
|
///
|
||||||
|
/// Typically, the compositor will want to use its normal mechanism
|
||||||
|
/// to manage keyboard focus between layer shell surfaces
|
||||||
|
/// with this setting and regular toplevels on the desktop layer (e.g. click to focus).
|
||||||
|
/// Nevertheless, it is possible for a compositor to require a special interaction
|
||||||
|
/// to focus or unfocus layer shell surfaces
|
||||||
|
///
|
||||||
|
/// This setting is mainly intended for desktop shell components that allow keyboard interaction.
|
||||||
|
/// Using this option can allow implementing a desktop shell that can be fully usable without the mouse.
|
||||||
|
OnDemand,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for KeyboardInteractivity {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<zwlr_layer_surface_v1::KeyboardInteractivity> for KeyboardInteractivity {
|
||||||
|
type Error = (zwlr_layer_surface_v1::Error, String);
|
||||||
|
|
||||||
|
fn try_from(ki: zwlr_layer_surface_v1::KeyboardInteractivity) -> Result<Self, Self::Error> {
|
||||||
|
match ki {
|
||||||
|
zwlr_layer_surface_v1::KeyboardInteractivity::None => Ok(Self::None),
|
||||||
|
zwlr_layer_surface_v1::KeyboardInteractivity::Exclusive => Ok(Self::Exclusive),
|
||||||
|
zwlr_layer_surface_v1::KeyboardInteractivity::OnDemand => Ok(Self::OnDemand),
|
||||||
|
_ => Err((
|
||||||
|
zwlr_layer_surface_v1::Error::InvalidKeyboardInteractivity,
|
||||||
|
format!("wrong keyboard interactivity value: {:?}", ki),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags::bitflags! {
|
||||||
|
/// Anchor bitflags, describing how the layers surface should be positioned and sized
|
||||||
|
pub struct Anchor: u32 {
|
||||||
|
/// The top edge of the anchor rectangle
|
||||||
|
const TOP = 1;
|
||||||
|
/// The bottom edge of the anchor rectangle
|
||||||
|
const BOTTOM = 2;
|
||||||
|
/// The left edge of the anchor rectangle
|
||||||
|
const LEFT = 4;
|
||||||
|
/// The right edge of the anchor rectangle
|
||||||
|
const RIGHT = 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Anchor {
|
||||||
|
/// Check if anchored horizontally
|
||||||
|
///
|
||||||
|
/// If it is anchored to `left` and `right` anchor at the same time
|
||||||
|
/// it returns `true`
|
||||||
|
pub fn anchored_horizontally(&self) -> bool {
|
||||||
|
self.contains(Self::LEFT) && self.contains(Self::RIGHT)
|
||||||
|
}
|
||||||
|
/// Check if anchored vertically
|
||||||
|
///
|
||||||
|
/// If it is anchored to `top` and `bottom` anchor at the same time
|
||||||
|
/// it returns `true`
|
||||||
|
pub fn anchored_vertically(&self) -> bool {
|
||||||
|
self.contains(Self::TOP) && self.contains(Self::BOTTOM)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Anchor {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<zwlr_layer_surface_v1::Anchor> for Anchor {
|
||||||
|
type Error = (zwlr_layer_surface_v1::Error, String);
|
||||||
|
|
||||||
|
fn try_from(anchor: zwlr_layer_surface_v1::Anchor) -> Result<Self, Self::Error> {
|
||||||
|
match Anchor::from_bits(anchor.bits()) {
|
||||||
|
Some(a) => Ok(a),
|
||||||
|
_ => Err((
|
||||||
|
zwlr_layer_surface_v1::Error::InvalidAnchor,
|
||||||
|
format!("invalid anchor {:?}", anchor),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exclusive zone descriptor
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum ExclusiveZone {
|
||||||
|
/// Requests that the compositor avoids occluding an area with other surfaces.
|
||||||
|
///
|
||||||
|
/// A exclusive zone value is the distance from the edge in surface-local coordinates to consider exclusive.
|
||||||
|
///
|
||||||
|
/// A exclusive value is only meaningful if the surface is
|
||||||
|
/// anchored to one edge or an edge and both perpendicular edges.
|
||||||
|
///
|
||||||
|
/// If the surface is:
|
||||||
|
/// - not anchored
|
||||||
|
/// - anchored to only two perpendicular edges (a corner),
|
||||||
|
/// - anchored to only two parallel edges or anchored to all edges,
|
||||||
|
///
|
||||||
|
/// The exclusive value should be treated the same as [`ExclusiveZone::Neutral`].
|
||||||
|
Exclusive(u32),
|
||||||
|
/// If set to Neutral,
|
||||||
|
/// the surface indicates that it would like to be moved to avoid occluding surfaces with a exclusive zone.
|
||||||
|
Neutral,
|
||||||
|
/// If set to DontCare,
|
||||||
|
/// the surface indicates that it would not like to be moved to accommodate for other surfaces,
|
||||||
|
/// and the compositor should extend it all the way to the edges it is anchored to.
|
||||||
|
DontCare,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ExclusiveZone {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Neutral
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i32> for ExclusiveZone {
|
||||||
|
fn from(v: i32) -> Self {
|
||||||
|
match v.cmp(&0) {
|
||||||
|
Ordering::Greater => Self::Exclusive(v as u32),
|
||||||
|
Ordering::Equal => Self::Neutral,
|
||||||
|
Ordering::Less => Self::DontCare,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ExclusiveZone> for i32 {
|
||||||
|
fn from(z: ExclusiveZone) -> i32 {
|
||||||
|
match z {
|
||||||
|
ExclusiveZone::Exclusive(v) => v as i32,
|
||||||
|
ExclusiveZone::Neutral => 0,
|
||||||
|
ExclusiveZone::DontCare => -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes distance from the anchor point of the output, in surface-local coordinates.
|
||||||
|
///
|
||||||
|
/// If surface did not anchor curtain edge, margin for that edge should be ignored.
|
||||||
|
///
|
||||||
|
/// The exclusive zone should includes the margins.
|
||||||
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
|
pub struct Margins {
|
||||||
|
/// Distance from [`Anchor::TOP`]
|
||||||
|
pub top: i32,
|
||||||
|
/// Distance from [`Anchor::TOP`]
|
||||||
|
pub right: i32,
|
||||||
|
/// Distance from [`Anchor::BOTTOM`]
|
||||||
|
pub bottom: i32,
|
||||||
|
/// Distance from [`Anchor::LEFT`]
|
||||||
|
pub left: i32,
|
||||||
|
}
|
|
@ -89,7 +89,7 @@ use wayland_server::{
|
||||||
use super::PingError;
|
use super::PingError;
|
||||||
|
|
||||||
// handlers for the xdg_shell protocol
|
// handlers for the xdg_shell protocol
|
||||||
mod xdg_handlers;
|
pub(super) mod xdg_handlers;
|
||||||
// compatibility handlers for the zxdg_shell_v6 protocol, its earlier version
|
// compatibility handlers for the zxdg_shell_v6 protocol, its earlier version
|
||||||
mod zxdgv6_handlers;
|
mod zxdgv6_handlers;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue