desktop: handle xdg-popups
This commit is contained in:
parent
90f7d53a3a
commit
f55f1bbbe0
|
@ -32,6 +32,8 @@ use crate::backend::egl::{
|
||||||
Error as EglError,
|
Error as EglError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
/// Possible transformations to two-dimensional planes
|
/// Possible transformations to two-dimensional planes
|
||||||
pub enum Transform {
|
pub enum Transform {
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
use crate::{
|
||||||
|
backend::renderer::{buffer_dimensions, Frame, ImportAll, Renderer, Texture},
|
||||||
|
utils::{Logical, Physical, Point, Size},
|
||||||
|
wayland::compositor::{
|
||||||
|
is_sync_subsurface, with_surface_tree_upward, BufferAssignment, Damage, SubsurfaceCachedState,
|
||||||
|
SurfaceAttributes, TraversalAction,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use wayland_server::protocol::{wl_buffer::WlBuffer, wl_surface::WlSurface};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(crate) struct SurfaceState {
|
||||||
|
pub(crate) buffer_dimensions: Option<Size<i32, Physical>>,
|
||||||
|
pub(crate) buffer_scale: i32,
|
||||||
|
pub(crate) buffer: Option<WlBuffer>,
|
||||||
|
pub(crate) texture: Option<Box<dyn std::any::Any + 'static>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SurfaceState {
|
||||||
|
pub fn update_buffer(&mut self, attrs: &mut SurfaceAttributes) {
|
||||||
|
match attrs.buffer.take() {
|
||||||
|
Some(BufferAssignment::NewBuffer { buffer, .. }) => {
|
||||||
|
// new contents
|
||||||
|
self.buffer_dimensions = buffer_dimensions(&buffer);
|
||||||
|
self.buffer_scale = attrs.buffer_scale;
|
||||||
|
if let Some(old_buffer) = std::mem::replace(&mut self.buffer, Some(buffer)) {
|
||||||
|
if &old_buffer != self.buffer.as_ref().unwrap() {
|
||||||
|
old_buffer.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.texture = None;
|
||||||
|
}
|
||||||
|
Some(BufferAssignment::Removed) => {
|
||||||
|
// remove the contents
|
||||||
|
self.buffer_dimensions = None;
|
||||||
|
if let Some(buffer) = self.buffer.take() {
|
||||||
|
buffer.release();
|
||||||
|
};
|
||||||
|
self.texture = None;
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_commit_buffer_handler(surface: &WlSurface) {
|
||||||
|
if !is_sync_subsurface(surface) {
|
||||||
|
with_surface_tree_upward(
|
||||||
|
surface,
|
||||||
|
(),
|
||||||
|
|_, _, _| TraversalAction::DoChildren(()),
|
||||||
|
|_surf, states, _| {
|
||||||
|
states
|
||||||
|
.data_map
|
||||||
|
.insert_if_missing(|| RefCell::new(SurfaceState::default()));
|
||||||
|
let mut data = states
|
||||||
|
.data_map
|
||||||
|
.get::<RefCell<SurfaceState>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow_mut();
|
||||||
|
data.update_buffer(&mut *states.cached_state.current::<SurfaceAttributes>());
|
||||||
|
},
|
||||||
|
|_, _, _| true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_surface_tree<R, E, F, T>(
|
||||||
|
renderer: &mut R,
|
||||||
|
frame: &mut F,
|
||||||
|
surface: &WlSurface,
|
||||||
|
scale: f64,
|
||||||
|
location: Point<i32, Logical>,
|
||||||
|
log: &slog::Logger,
|
||||||
|
) -> Result<(), R::Error>
|
||||||
|
where
|
||||||
|
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportAll,
|
||||||
|
F: Frame<Error = E, TextureId = T>,
|
||||||
|
E: std::error::Error,
|
||||||
|
T: Texture + 'static,
|
||||||
|
{
|
||||||
|
let mut result = Ok(());
|
||||||
|
with_surface_tree_upward(
|
||||||
|
surface,
|
||||||
|
location,
|
||||||
|
|_surface, states, location| {
|
||||||
|
let mut location = *location;
|
||||||
|
if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
|
||||||
|
let mut data = data.borrow_mut();
|
||||||
|
let attributes = states.cached_state.current::<SurfaceAttributes>();
|
||||||
|
// Import a new buffer if necessary
|
||||||
|
if data.texture.is_none() {
|
||||||
|
if let Some(buffer) = data.buffer.as_ref() {
|
||||||
|
let damage = attributes
|
||||||
|
.damage
|
||||||
|
.iter()
|
||||||
|
.map(|dmg| match dmg {
|
||||||
|
Damage::Buffer(rect) => *rect,
|
||||||
|
// TODO also apply transformations
|
||||||
|
Damage::Surface(rect) => rect.to_buffer(attributes.buffer_scale),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
match renderer.import_buffer(buffer, Some(states), &damage) {
|
||||||
|
Some(Ok(m)) => {
|
||||||
|
data.texture = Some(Box::new(m));
|
||||||
|
}
|
||||||
|
Some(Err(err)) => {
|
||||||
|
slog::warn!(log, "Error loading buffer: {}", err);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
slog::error!(log, "Unknown buffer format for: {:?}", buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Now, should we be drawn ?
|
||||||
|
if data.texture.is_some() {
|
||||||
|
// if yes, also process the children
|
||||||
|
if states.role == Some("subsurface") {
|
||||||
|
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
||||||
|
location += current.location;
|
||||||
|
}
|
||||||
|
TraversalAction::DoChildren(location)
|
||||||
|
} else {
|
||||||
|
// we are not displayed, so our children are neither
|
||||||
|
TraversalAction::SkipChildren
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we are not displayed, so our children are neither
|
||||||
|
TraversalAction::SkipChildren
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|surface, states, location| {
|
||||||
|
let mut location = *location;
|
||||||
|
if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
|
||||||
|
let mut data = data.borrow_mut();
|
||||||
|
let buffer_scale = data.buffer_scale;
|
||||||
|
let attributes = states.cached_state.current::<SurfaceAttributes>();
|
||||||
|
if let Some(texture) = data.texture.as_mut().and_then(|x| x.downcast_mut::<T>()) {
|
||||||
|
// we need to re-extract the subsurface offset, as the previous closure
|
||||||
|
// only passes it to our children
|
||||||
|
if states.role == Some("subsurface") {
|
||||||
|
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
||||||
|
location += current.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Take wp_viewporter into account
|
||||||
|
if let Err(err) = frame.render_texture_at(
|
||||||
|
texture,
|
||||||
|
location.to_f64().to_physical(scale).to_i32_round(),
|
||||||
|
buffer_scale,
|
||||||
|
scale,
|
||||||
|
attributes.buffer_transform.into(),
|
||||||
|
1.0,
|
||||||
|
) {
|
||||||
|
result = Err(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|_, _, _| true,
|
||||||
|
);
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
// TODO: Remove - but for now, this makes sure these files are not completely highlighted with warnings
|
// TODO: Remove - but for now, this makes sure these files are not completely highlighted with warnings
|
||||||
#![allow(missing_docs, clippy::all)]
|
#![allow(missing_docs, clippy::all)]
|
||||||
|
mod popup;
|
||||||
mod space;
|
mod space;
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
|
pub use self::popup::*;
|
||||||
pub use self::space::*;
|
pub use self::space::*;
|
||||||
pub use self::window::*;
|
pub use self::window::*;
|
||||||
|
|
|
@ -0,0 +1,262 @@
|
||||||
|
use crate::{
|
||||||
|
utils::{DeadResource, Logical, Point},
|
||||||
|
wayland::{
|
||||||
|
compositor::{get_role, with_states},
|
||||||
|
shell::xdg::{PopupSurface, XdgPopupSurfaceRoleAttributes, XDG_POPUP_ROLE},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use wayland_server::protocol::wl_surface::WlSurface;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PopupManager {
|
||||||
|
unmapped_popups: Vec<PopupKind>,
|
||||||
|
popup_trees: Vec<PopupTree>,
|
||||||
|
logger: ::slog::Logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PopupManager {
|
||||||
|
pub fn new<L: Into<Option<::slog::Logger>>>(logger: L) -> Self {
|
||||||
|
PopupManager {
|
||||||
|
unmapped_popups: Vec::new(),
|
||||||
|
popup_trees: Vec::new(),
|
||||||
|
logger: crate::slog_or_fallback(logger),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn track_popup(&mut self, kind: PopupKind) -> Result<(), DeadResource> {
|
||||||
|
if kind.parent().is_some() {
|
||||||
|
self.add_popup(kind)
|
||||||
|
} else {
|
||||||
|
slog::trace!(self.logger, "Adding unmapped popups: {:?}", kind);
|
||||||
|
self.unmapped_popups.push(kind);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn commit(&mut self, surface: &WlSurface) {
|
||||||
|
if get_role(surface) == Some(XDG_POPUP_ROLE) {
|
||||||
|
if let Some(i) = self
|
||||||
|
.unmapped_popups
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, p)| p.get_surface() == Some(surface))
|
||||||
|
.map(|(i, _)| i)
|
||||||
|
{
|
||||||
|
slog::trace!(self.logger, "Popup got mapped");
|
||||||
|
let popup = self.unmapped_popups.swap_remove(i);
|
||||||
|
// at this point the popup must have a parent,
|
||||||
|
// or it would have raised a protocol error
|
||||||
|
let _ = self.add_popup(popup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_popup(&mut self, popup: PopupKind) -> Result<(), DeadResource> {
|
||||||
|
let mut parent = popup.parent().unwrap();
|
||||||
|
while get_role(&parent) == Some(XDG_POPUP_ROLE) {
|
||||||
|
parent = with_states(&parent, |states| {
|
||||||
|
states
|
||||||
|
.data_map
|
||||||
|
.get::<Mutex<XdgPopupSurfaceRoleAttributes>>()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.parent
|
||||||
|
.as_ref()
|
||||||
|
.cloned()
|
||||||
|
.unwrap()
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
with_states(&parent, |states| {
|
||||||
|
let tree = PopupTree::default();
|
||||||
|
if states.data_map.insert_if_missing(|| tree.clone()) {
|
||||||
|
self.popup_trees.push(tree);
|
||||||
|
};
|
||||||
|
let tree = states.data_map.get::<PopupTree>().unwrap();
|
||||||
|
if !tree.alive() {
|
||||||
|
// if it previously had no popups, we likely removed it from our list already
|
||||||
|
self.popup_trees.push(tree.clone());
|
||||||
|
}
|
||||||
|
slog::trace!(self.logger, "Adding popup {:?} to parent {:?}", popup, parent);
|
||||||
|
tree.insert(popup);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_popup(&self, surface: &WlSurface) -> Option<PopupKind> {
|
||||||
|
self.unmapped_popups
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.get_surface() == Some(surface))
|
||||||
|
.cloned()
|
||||||
|
.or_else(|| {
|
||||||
|
self.popup_trees
|
||||||
|
.iter()
|
||||||
|
.map(|tree| tree.iter_popups())
|
||||||
|
.flatten()
|
||||||
|
.find(|(p, _)| p.get_surface() == Some(surface))
|
||||||
|
.map(|(p, _)| p)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn popups_for_surface(
|
||||||
|
surface: &WlSurface,
|
||||||
|
) -> Result<impl Iterator<Item = (PopupKind, Point<i32, Logical>)>, DeadResource> {
|
||||||
|
with_states(surface, |states| {
|
||||||
|
states
|
||||||
|
.data_map
|
||||||
|
.get::<PopupTree>()
|
||||||
|
.map(|x| x.iter_popups())
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cleanup(&mut self) {
|
||||||
|
// retain_mut is sadly still unstable
|
||||||
|
self.popup_trees.iter_mut().for_each(|tree| tree.cleanup());
|
||||||
|
self.popup_trees.retain(|tree| tree.alive());
|
||||||
|
self.unmapped_popups.retain(|surf| surf.alive());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
struct PopupTree(Arc<Mutex<Vec<PopupNode>>>);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct PopupNode {
|
||||||
|
surface: PopupKind,
|
||||||
|
children: Vec<PopupNode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PopupTree {
|
||||||
|
fn iter_popups(&self) -> impl Iterator<Item = (PopupKind, Point<i32, Logical>)> {
|
||||||
|
self.0
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|n| n.iter_popups_relative_to((0, 0)).map(|(p, l)| (p.clone(), l)))
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert(&self, popup: PopupKind) {
|
||||||
|
let children = &mut *self.0.lock().unwrap();
|
||||||
|
for child in children.iter_mut() {
|
||||||
|
if child.insert(popup.clone()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
children.push(PopupNode::new(popup));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cleanup(&mut self) {
|
||||||
|
let mut children = self.0.lock().unwrap();
|
||||||
|
for child in children.iter_mut() {
|
||||||
|
child.cleanup();
|
||||||
|
}
|
||||||
|
children.retain(|n| n.surface.alive());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alive(&self) -> bool {
|
||||||
|
!self.0.lock().unwrap().is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PopupNode {
|
||||||
|
fn new(surface: PopupKind) -> Self {
|
||||||
|
PopupNode {
|
||||||
|
surface,
|
||||||
|
children: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iter_popups_relative_to<P: Into<Point<i32, Logical>>>(
|
||||||
|
&self,
|
||||||
|
loc: P,
|
||||||
|
) -> impl Iterator<Item = (&PopupKind, Point<i32, Logical>)> {
|
||||||
|
let relative_to = loc.into() + self.surface.location();
|
||||||
|
std::iter::once((&self.surface, relative_to)).chain(
|
||||||
|
self.children
|
||||||
|
.iter()
|
||||||
|
.map(move |x| {
|
||||||
|
Box::new(x.iter_popups_relative_to(relative_to))
|
||||||
|
as Box<dyn Iterator<Item = (&PopupKind, Point<i32, Logical>)>>
|
||||||
|
})
|
||||||
|
.flatten(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert(&mut self, popup: PopupKind) -> bool {
|
||||||
|
let parent = popup.parent().unwrap();
|
||||||
|
if self.surface.get_surface() == Some(&parent) {
|
||||||
|
self.children.push(PopupNode::new(popup));
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
for child in &mut self.children {
|
||||||
|
if child.insert(popup.clone()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cleanup(&mut self) {
|
||||||
|
for child in &mut self.children {
|
||||||
|
child.cleanup();
|
||||||
|
}
|
||||||
|
self.children.retain(|n| n.surface.alive());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum PopupKind {
|
||||||
|
Xdg(PopupSurface),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PopupKind {
|
||||||
|
fn alive(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
PopupKind::Xdg(ref t) => t.alive(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_surface(&self) -> Option<&WlSurface> {
|
||||||
|
match *self {
|
||||||
|
PopupKind::Xdg(ref t) => t.get_surface(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parent(&self) -> Option<WlSurface> {
|
||||||
|
match *self {
|
||||||
|
PopupKind::Xdg(ref t) => t.get_parent_surface(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn location(&self) -> Point<i32, Logical> {
|
||||||
|
let wl_surface = match self.get_surface() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return (0, 0).into(),
|
||||||
|
};
|
||||||
|
with_states(wl_surface, |states| {
|
||||||
|
states
|
||||||
|
.data_map
|
||||||
|
.get::<Mutex<XdgPopupSurfaceRoleAttributes>>()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.current
|
||||||
|
.geometry
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
.loc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PopupSurface> for PopupKind {
|
||||||
|
fn from(p: PopupSurface) -> PopupKind {
|
||||||
|
PopupKind::Xdg(p)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use super::{draw_window, SurfaceState, Window};
|
use super::{draw_window, Window};
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::renderer::{Frame, ImportAll, Renderer, Transform},
|
backend::renderer::{utils::SurfaceState, Frame, ImportAll, Renderer, Transform},
|
||||||
utils::{Logical, Point, Rectangle},
|
utils::{Logical, Point, Rectangle},
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::{with_surface_tree_downward, SubsurfaceCachedState, TraversalAction},
|
compositor::{with_surface_tree_downward, SubsurfaceCachedState, TraversalAction},
|
||||||
|
@ -150,7 +150,7 @@ impl Space {
|
||||||
|
|
||||||
/// Get a reference to the window under a given point, if any
|
/// Get a reference to the window under a given point, if any
|
||||||
pub fn window_under(&self, point: Point<f64, Logical>) -> Option<&Window> {
|
pub fn window_under(&self, point: Point<f64, Logical>) -> Option<&Window> {
|
||||||
self.windows.iter().find(|w| {
|
self.windows.iter().rev().find(|w| {
|
||||||
let bbox = window_rect(w, &self.id);
|
let bbox = window_rect(w, &self.id);
|
||||||
bbox.to_f64().contains(point)
|
bbox.to_f64().contains(point)
|
||||||
})
|
})
|
||||||
|
@ -419,7 +419,7 @@ impl Space {
|
||||||
|
|
||||||
// lets iterate front to back and figure out, what new windows or unmoved windows we have
|
// lets iterate front to back and figure out, what new windows or unmoved windows we have
|
||||||
for window in self.windows.iter().rev() {
|
for window in self.windows.iter().rev() {
|
||||||
let geo = window_rect(window, &self.id);
|
let geo = window_rect_with_popups(window, &self.id);
|
||||||
let old_geo = state.last_state.get(&window.0.id).cloned();
|
let old_geo = state.last_state.get(&window.0.id).cloned();
|
||||||
|
|
||||||
// window was moved or resized
|
// window was moved or resized
|
||||||
|
@ -481,7 +481,7 @@ impl Space {
|
||||||
|
|
||||||
// Then re-draw all window overlapping with a damage rect.
|
// Then re-draw all window overlapping with a damage rect.
|
||||||
for window in self.windows.iter() {
|
for window in self.windows.iter() {
|
||||||
let wgeo = window_rect(window, &self.id);
|
let wgeo = window_rect_with_popups(window, &self.id);
|
||||||
let mut loc = window_loc(window, &self.id);
|
let mut loc = window_loc(window, &self.id);
|
||||||
if damage.iter().any(|geo| wgeo.overlaps(*geo)) {
|
if damage.iter().any(|geo| wgeo.overlaps(*geo)) {
|
||||||
loc -= output_geo.loc;
|
loc -= output_geo.loc;
|
||||||
|
@ -506,7 +506,7 @@ impl Space {
|
||||||
.windows
|
.windows
|
||||||
.iter()
|
.iter()
|
||||||
.map(|window| {
|
.map(|window| {
|
||||||
let wgeo = window_rect(window, &self.id);
|
let wgeo = window_rect_with_popups(window, &self.id);
|
||||||
(window.0.id, wgeo)
|
(window.0.id, wgeo)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -550,6 +550,13 @@ fn window_rect(window: &Window, space_id: &usize) -> Rectangle<i32, Logical> {
|
||||||
wgeo
|
wgeo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn window_rect_with_popups(window: &Window, space_id: &usize) -> Rectangle<i32, Logical> {
|
||||||
|
let loc = window_loc(window, space_id);
|
||||||
|
let mut wgeo = window.bbox_with_popups();
|
||||||
|
wgeo.loc += loc;
|
||||||
|
wgeo
|
||||||
|
}
|
||||||
|
|
||||||
fn window_loc(window: &Window, space_id: &usize) -> Point<i32, Logical> {
|
fn window_loc(window: &Window, space_id: &usize) -> Point<i32, Logical> {
|
||||||
window
|
window
|
||||||
.user_data()
|
.user_data()
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::renderer::{buffer_dimensions, Frame, ImportAll, Renderer, Texture},
|
backend::renderer::{
|
||||||
utils::{Logical, Physical, Point, Rectangle, Size},
|
utils::{draw_surface_tree, SurfaceState},
|
||||||
|
Frame, ImportAll, Renderer, Texture,
|
||||||
|
},
|
||||||
|
desktop::PopupManager,
|
||||||
|
utils::{Logical, Point, Rectangle, Size},
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::{
|
compositor::{
|
||||||
add_commit_hook, is_sync_subsurface, with_states, with_surface_tree_downward,
|
with_states, with_surface_tree_downward, with_surface_tree_upward, Damage, SubsurfaceCachedState,
|
||||||
with_surface_tree_upward, BufferAssignment, Damage, SubsurfaceCachedState, SurfaceAttributes,
|
SurfaceAttributes, TraversalAction,
|
||||||
TraversalAction,
|
|
||||||
},
|
},
|
||||||
shell::xdg::{SurfaceCachedState, ToplevelSurface},
|
shell::xdg::{SurfaceCachedState, ToplevelSurface},
|
||||||
},
|
},
|
||||||
|
@ -98,62 +101,7 @@ impl Kind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub(super) struct SurfaceState {
|
|
||||||
buffer_dimensions: Option<Size<i32, Physical>>,
|
|
||||||
buffer_scale: i32,
|
|
||||||
buffer: Option<wl_buffer::WlBuffer>,
|
|
||||||
texture: Option<Box<dyn std::any::Any + 'static>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn surface_commit(surface: &wl_surface::WlSurface) {
|
|
||||||
if !is_sync_subsurface(surface) {
|
|
||||||
with_surface_tree_upward(
|
|
||||||
surface,
|
|
||||||
(),
|
|
||||||
|_, _, _| TraversalAction::DoChildren(()),
|
|
||||||
|_surf, states, _| {
|
|
||||||
states
|
|
||||||
.data_map
|
|
||||||
.insert_if_missing(|| RefCell::new(SurfaceState::default()));
|
|
||||||
let mut data = states
|
|
||||||
.data_map
|
|
||||||
.get::<RefCell<SurfaceState>>()
|
|
||||||
.unwrap()
|
|
||||||
.borrow_mut();
|
|
||||||
data.update_buffer(&mut *states.cached_state.current::<SurfaceAttributes>());
|
|
||||||
},
|
|
||||||
|_, _, _| true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SurfaceState {
|
impl SurfaceState {
|
||||||
pub fn update_buffer(&mut self, attrs: &mut SurfaceAttributes) {
|
|
||||||
match attrs.buffer.take() {
|
|
||||||
Some(BufferAssignment::NewBuffer { buffer, .. }) => {
|
|
||||||
// new contents
|
|
||||||
self.buffer_dimensions = buffer_dimensions(&buffer);
|
|
||||||
self.buffer_scale = attrs.buffer_scale;
|
|
||||||
if let Some(old_buffer) = std::mem::replace(&mut self.buffer, Some(buffer)) {
|
|
||||||
if &old_buffer != self.buffer.as_ref().unwrap() {
|
|
||||||
old_buffer.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.texture = None;
|
|
||||||
}
|
|
||||||
Some(BufferAssignment::Removed) => {
|
|
||||||
// remove the contents
|
|
||||||
self.buffer_dimensions = None;
|
|
||||||
if let Some(buffer) = self.buffer.take() {
|
|
||||||
buffer.release();
|
|
||||||
};
|
|
||||||
self.texture = None;
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the size of the surface.
|
/// Returns the size of the surface.
|
||||||
pub fn size(&self) -> Option<Size<i32, Logical>> {
|
pub fn size(&self) -> Option<Size<i32, Logical>> {
|
||||||
self.buffer_dimensions
|
self.buffer_dimensions
|
||||||
|
@ -248,34 +196,25 @@ impl Window {
|
||||||
/// A bounding box over this window and its children.
|
/// A bounding box over this window and its children.
|
||||||
// TODO: Cache and document when to trigger updates. If possible let space do it
|
// TODO: Cache and document when to trigger updates. If possible let space do it
|
||||||
pub fn bbox(&self) -> Rectangle<i32, Logical> {
|
pub fn bbox(&self) -> Rectangle<i32, Logical> {
|
||||||
let mut bounding_box = Rectangle::from_loc_and_size((0, 0), (0, 0));
|
if let Some(surface) = self.0.toplevel.get_surface() {
|
||||||
if let Some(wl_surface) = self.0.toplevel.get_surface() {
|
bbox_from_surface_tree(surface, (0, 0))
|
||||||
with_surface_tree_downward(
|
|
||||||
wl_surface,
|
|
||||||
(0, 0).into(),
|
|
||||||
|_, states, loc: &Point<i32, Logical>| {
|
|
||||||
let mut loc = *loc;
|
|
||||||
let data = states.data_map.get::<RefCell<SurfaceState>>();
|
|
||||||
|
|
||||||
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 {
|
} else {
|
||||||
// If the parent surface is unmapped, then the child surfaces are hidden as
|
Rectangle::from_loc_and_size((0, 0), (0, 0))
|
||||||
// well, no need to consider them here.
|
}
|
||||||
TraversalAction::SkipChildren
|
}
|
||||||
|
|
||||||
|
pub fn bbox_with_popups(&self) -> Rectangle<i32, Logical> {
|
||||||
|
let mut bounding_box = self.bbox();
|
||||||
|
if let Some(surface) = self.0.toplevel.get_surface() {
|
||||||
|
for (popup, location) in PopupManager::popups_for_surface(surface)
|
||||||
|
.ok()
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
{
|
||||||
|
if let Some(surface) = popup.get_surface() {
|
||||||
|
bounding_box = bounding_box.merge(bbox_from_surface_tree(surface, location));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|_, _, _| {},
|
|
||||||
|_, _, _| true,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
bounding_box
|
bounding_box
|
||||||
}
|
}
|
||||||
|
@ -310,25 +249,17 @@ impl Window {
|
||||||
/// 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) {
|
pub fn send_frame(&self, time: u32) {
|
||||||
if let Some(wl_surface) = self.0.toplevel.get_surface() {
|
if let Some(surface) = self.0.toplevel.get_surface() {
|
||||||
with_surface_tree_downward(
|
send_frames_surface_tree(surface, time);
|
||||||
wl_surface,
|
for (popup, _) in PopupManager::popups_for_surface(surface)
|
||||||
(),
|
.ok()
|
||||||
|_, _, &()| TraversalAction::DoChildren(()),
|
.into_iter()
|
||||||
|_surf, states, &()| {
|
.flatten()
|
||||||
// the surface may not have any user_data if it is a subsurface and has not
|
|
||||||
// yet been commited
|
|
||||||
for callback in states
|
|
||||||
.cached_state
|
|
||||||
.current::<SurfaceAttributes>()
|
|
||||||
.frame_callbacks
|
|
||||||
.drain(..)
|
|
||||||
{
|
{
|
||||||
callback.done(time);
|
if let Some(surface) = popup.get_surface() {
|
||||||
|
send_frames_surface_tree(surface, time);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|_, _, &()| true,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,50 +269,62 @@ impl Window {
|
||||||
&self,
|
&self,
|
||||||
point: Point<f64, Logical>,
|
point: Point<f64, Logical>,
|
||||||
) -> Option<(wl_surface::WlSurface, Point<i32, Logical>)> {
|
) -> Option<(wl_surface::WlSurface, Point<i32, Logical>)> {
|
||||||
let found = RefCell::new(None);
|
if let Some(surface) = self.0.toplevel.get_surface() {
|
||||||
if let Some(wl_surface) = self.0.toplevel.get_surface() {
|
for (popup, location) in PopupManager::popups_for_surface(surface)
|
||||||
with_surface_tree_downward(
|
.ok()
|
||||||
wl_surface,
|
.into_iter()
|
||||||
(0, 0).into(),
|
.flatten()
|
||||||
|wl_surface, states, location: &Point<i32, Logical>| {
|
{
|
||||||
let mut location = *location;
|
if let Some(result) = popup
|
||||||
let data = states.data_map.get::<RefCell<SurfaceState>>();
|
.get_surface()
|
||||||
|
.and_then(|surface| under_from_surface_tree(surface, point, location))
|
||||||
if states.role == Some("subsurface") {
|
{
|
||||||
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
return Some(result);
|
||||||
location += current.location;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let contains_the_point = data
|
under_from_surface_tree(surface, point, (0, 0))
|
||||||
.map(|data| {
|
} else {
|
||||||
data.borrow()
|
None
|
||||||
.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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Damage of all the surfaces of this window
|
/// Damage of all the surfaces of this window
|
||||||
pub(super) fn accumulated_damage(&self) -> Vec<Rectangle<i32, Logical>> {
|
pub(super) fn accumulated_damage(&self) -> Vec<Rectangle<i32, Logical>> {
|
||||||
let mut damage = Vec::new();
|
let mut damage = Vec::new();
|
||||||
let location = (0, 0).into();
|
|
||||||
if let Some(surface) = self.0.toplevel.get_surface() {
|
if let Some(surface) = self.0.toplevel.get_surface() {
|
||||||
|
damage.extend(damage_from_surface_tree(surface, (0, 0)));
|
||||||
|
for (popup, location) in PopupManager::popups_for_surface(surface)
|
||||||
|
.ok()
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
{
|
||||||
|
if let Some(surface) = popup.get_surface() {
|
||||||
|
let popup_damage = damage_from_surface_tree(surface, location);
|
||||||
|
damage.extend(popup_damage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
damage
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toplevel(&self) -> &Kind {
|
||||||
|
&self.0.toplevel
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn user_data(&self) -> &UserDataMap {
|
||||||
|
&self.0.user_data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn damage_from_surface_tree<P>(surface: &wl_surface::WlSurface, location: P) -> Vec<Rectangle<i32, Logical>>
|
||||||
|
where
|
||||||
|
P: Into<Point<i32, Logical>>,
|
||||||
|
{
|
||||||
|
let mut damage = Vec::new();
|
||||||
with_surface_tree_upward(
|
with_surface_tree_upward(
|
||||||
surface,
|
surface,
|
||||||
location,
|
location.into(),
|
||||||
|_surface, states, location| {
|
|_surface, states, location| {
|
||||||
let mut location = *location;
|
let mut location = *location;
|
||||||
if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
|
if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
|
||||||
|
@ -420,44 +363,107 @@ impl Window {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|_, _, _| true,
|
|_, _, _| true,
|
||||||
)
|
);
|
||||||
}
|
|
||||||
damage
|
damage
|
||||||
}
|
|
||||||
|
|
||||||
pub fn toplevel(&self) -> &Kind {
|
|
||||||
&self.0.toplevel
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn user_data(&self) -> &UserDataMap {
|
|
||||||
&self.0.user_data
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Has to be called on commit - Window handles the buffer for you
|
|
||||||
pub fn commit(surface: &wl_surface::WlSurface) {
|
|
||||||
surface_commit(surface)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This is basically `draw_surface_tree` from anvil.
|
fn bbox_from_surface_tree<P>(surface: &wl_surface::WlSurface, location: P) -> Rectangle<i32, Logical>
|
||||||
// Can we move this somewhere, where it is also usable for other things then windows?
|
where
|
||||||
// Maybe add this as a helper function for surfaces to `backend::renderer`?
|
P: Into<Point<i32, Logical>>,
|
||||||
// How do we handle SurfaceState in that case? Like we need a closure to
|
{
|
||||||
// store and retrieve textures for arbitrary surface trees? Or leave this to the
|
let location = location.into();
|
||||||
// compositor, but that feels like a lot of unnecessary code dublication.
|
let mut bounding_box = Rectangle::from_loc_and_size(location, (0, 0));
|
||||||
|
with_surface_tree_downward(
|
||||||
|
surface,
|
||||||
|
location,
|
||||||
|
|_, states, loc: &Point<i32, Logical>| {
|
||||||
|
let mut loc = *loc;
|
||||||
|
let data = states.data_map.get::<RefCell<SurfaceState>>();
|
||||||
|
|
||||||
// TODO: This does not handle ImportAll errors properly and uses only one texture slot.
|
if let Some(size) = data.and_then(|d| d.borrow().size()) {
|
||||||
// This means it is *not* compatible with MultiGPU setups at all.
|
if states.role == Some("subsurface") {
|
||||||
// Current plan is to make sure it does not crash at least in that case and later add
|
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
||||||
// A `MultiGpuManager` that opens gpus automatically, creates renderers for them,
|
loc += current.location;
|
||||||
// implements `Renderer` and `ImportAll` itself and dispatches everything accordingly,
|
}
|
||||||
// even copying buffers if necessary. This abstraction will likely also handle dmabuf-
|
|
||||||
// protocol(s) (and maybe explicit sync?). Then this function will be fine and all the
|
|
||||||
// gore of handling multiple gpus will be hidden away for most if not all cases.
|
|
||||||
|
|
||||||
// TODO: This function does not crop or scale windows to fit into a space.
|
// Update the bounding box.
|
||||||
// How do we want to handle this? Add an additional size property to a window?
|
bounding_box = bounding_box.merge(Rectangle::from_loc_and_size(loc, size));
|
||||||
// Let the user specify the max size and the method to handle it?
|
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
bounding_box
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn under_from_surface_tree<P>(
|
||||||
|
surface: &wl_surface::WlSurface,
|
||||||
|
point: Point<f64, Logical>,
|
||||||
|
location: P,
|
||||||
|
) -> Option<(wl_surface::WlSurface, Point<i32, Logical>)>
|
||||||
|
where
|
||||||
|
P: Into<Point<i32, Logical>>,
|
||||||
|
{
|
||||||
|
let found = RefCell::new(None);
|
||||||
|
with_surface_tree_downward(
|
||||||
|
surface,
|
||||||
|
location.into(),
|
||||||
|
|wl_surface, states, location: &Point<i32, Logical>| {
|
||||||
|
let mut location = *location;
|
||||||
|
let data = states.data_map.get::<RefCell<SurfaceState>>();
|
||||||
|
|
||||||
|
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 send_frames_surface_tree(surface: &wl_surface::WlSurface, time: u32) {
|
||||||
|
with_surface_tree_downward(
|
||||||
|
surface,
|
||||||
|
(),
|
||||||
|
|_, _, &()| TraversalAction::DoChildren(()),
|
||||||
|
|_surf, states, &()| {
|
||||||
|
// the surface may not have any user_data if it is a subsurface and has not
|
||||||
|
// yet been commited
|
||||||
|
for callback in states
|
||||||
|
.cached_state
|
||||||
|
.current::<SurfaceAttributes>()
|
||||||
|
.frame_callbacks
|
||||||
|
.drain(..)
|
||||||
|
{
|
||||||
|
callback.done(time);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|_, _, &()| true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn draw_window<R, E, F, T>(
|
pub fn draw_window<R, E, F, T>(
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
|
@ -473,89 +479,17 @@ where
|
||||||
E: std::error::Error,
|
E: std::error::Error,
|
||||||
T: Texture + 'static,
|
T: Texture + 'static,
|
||||||
{
|
{
|
||||||
let mut result = Ok(());
|
if let Some(surface) = window.toplevel().get_surface() {
|
||||||
if let Some(surface) = window.0.toplevel.get_surface() {
|
draw_surface_tree(renderer, frame, surface, scale, location, log)?;
|
||||||
with_surface_tree_upward(
|
for (popup, p_location) in PopupManager::popups_for_surface(surface)
|
||||||
surface,
|
.ok()
|
||||||
location,
|
.into_iter()
|
||||||
|_surface, states, location| {
|
.flatten()
|
||||||
let mut location = *location;
|
{
|
||||||
if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
|
if let Some(surface) = popup.get_surface() {
|
||||||
let mut data = data.borrow_mut();
|
draw_surface_tree(renderer, frame, surface, scale, location + p_location, log)?;
|
||||||
let attributes = states.cached_state.current::<SurfaceAttributes>();
|
|
||||||
// Import a new buffer if necessary
|
|
||||||
if data.texture.is_none() {
|
|
||||||
if let Some(buffer) = data.buffer.as_ref() {
|
|
||||||
let damage = attributes
|
|
||||||
.damage
|
|
||||||
.iter()
|
|
||||||
.map(|dmg| match dmg {
|
|
||||||
Damage::Buffer(rect) => *rect,
|
|
||||||
// TODO also apply transformations
|
|
||||||
Damage::Surface(rect) => rect.to_buffer(attributes.buffer_scale),
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
match renderer.import_buffer(buffer, Some(states), &damage) {
|
|
||||||
Some(Ok(m)) => {
|
|
||||||
data.texture = Some(Box::new(m));
|
|
||||||
}
|
|
||||||
Some(Err(err)) => {
|
|
||||||
slog::warn!(log, "Error loading buffer: {}", err);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
slog::error!(log, "Unknown buffer format for: {:?}", buffer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
Ok(())
|
||||||
// Now, should we be drawn ?
|
|
||||||
if data.texture.is_some() {
|
|
||||||
// if yes, also process the children
|
|
||||||
if states.role == Some("subsurface") {
|
|
||||||
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
|
||||||
location += current.location;
|
|
||||||
}
|
|
||||||
TraversalAction::DoChildren(location)
|
|
||||||
} else {
|
|
||||||
// we are not displayed, so our children are neither
|
|
||||||
TraversalAction::SkipChildren
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// we are not displayed, so our children are neither
|
|
||||||
TraversalAction::SkipChildren
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|_surface, states, location| {
|
|
||||||
let mut location = *location;
|
|
||||||
if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
|
|
||||||
let mut data = data.borrow_mut();
|
|
||||||
let buffer_scale = data.buffer_scale;
|
|
||||||
let attributes = states.cached_state.current::<SurfaceAttributes>();
|
|
||||||
if let Some(texture) = data.texture.as_mut().and_then(|x| x.downcast_mut::<T>()) {
|
|
||||||
// we need to re-extract the subsurface offset, as the previous closure
|
|
||||||
// only passes it to our children
|
|
||||||
if states.role == Some("subsurface") {
|
|
||||||
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
|
||||||
location += current.location;
|
|
||||||
}
|
|
||||||
// TODO: Take wp_viewporter into account
|
|
||||||
if let Err(err) = frame.render_texture_at(
|
|
||||||
texture,
|
|
||||||
location.to_f64().to_physical(scale).to_i32_round(),
|
|
||||||
buffer_scale,
|
|
||||||
scale,
|
|
||||||
attributes.buffer_transform.into(),
|
|
||||||
1.0,
|
|
||||||
) {
|
|
||||||
result = Err(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|_, _, _| true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue