Retain surface textures for animations
This commit is contained in:
parent
3540cdc4be
commit
fba9d65ba1
8 changed files with 365 additions and 19 deletions
|
|
@ -101,6 +101,17 @@ Tests:
|
||||||
|
|
||||||
Goal: freeze visual contents during movement and enable spawn-out.
|
Goal: freeze visual contents during movement and enable spawn-out.
|
||||||
|
|
||||||
|
Initial retained-record implementation status:
|
||||||
|
|
||||||
|
- Tiled animation can retain GPU/dmabuf-backed XDG and Xwayland surface trees.
|
||||||
|
- Retained records hold both `GfxTexture` and `SurfaceBuffer` references so the
|
||||||
|
existing buffer release/sync path remains authoritative.
|
||||||
|
- Single-pixel buffers can be retained as color records.
|
||||||
|
- Async SHM textures are not retained yet because Wry's per-surface SHM
|
||||||
|
front/back textures can be reused by later commits while an animation is still
|
||||||
|
running. Those surfaces fall back to live rendering until an explicit offscreen
|
||||||
|
copy fallback exists.
|
||||||
|
|
||||||
Implementation shape:
|
Implementation shape:
|
||||||
|
|
||||||
- Add a retained render-record tree for toplevel surfaces.
|
- Add a retained render-record tree for toplevel surfaces.
|
||||||
|
|
|
||||||
139
src/animation.rs
139
src/animation.rs
|
|
@ -1,7 +1,11 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
cmm::{cmm_description::ColorDescription, cmm_render_intent::RenderIntent},
|
||||||
|
gfx_api::{GfxTexture, SampleRect},
|
||||||
|
ifs::wl_surface::{SurfaceBuffer, WlSurface},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
state::State,
|
state::State,
|
||||||
|
theme::Color,
|
||||||
tree::{LatchListener, NodeId, OutputNode},
|
tree::{LatchListener, NodeId, OutputNode},
|
||||||
utils::{clonecell::CloneCell, event_listener::EventListener},
|
utils::{clonecell::CloneCell, event_listener::EventListener},
|
||||||
},
|
},
|
||||||
|
|
@ -54,6 +58,112 @@ pub struct AnimationState {
|
||||||
tick: CloneCell<Option<Rc<AnimationTick>>>,
|
tick: CloneCell<Option<Rc<AnimationTick>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RetainedToplevel {
|
||||||
|
pub offset: (i32, i32),
|
||||||
|
pub surface: RetainedSurface,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RetainedSurface {
|
||||||
|
pub offset: (i32, i32),
|
||||||
|
pub size: (i32, i32),
|
||||||
|
pub content: RetainedContent,
|
||||||
|
pub below: Vec<RetainedSurface>,
|
||||||
|
pub above: Vec<RetainedSurface>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum RetainedContent {
|
||||||
|
Texture {
|
||||||
|
texture: Rc<dyn GfxTexture>,
|
||||||
|
buffer: Rc<SurfaceBuffer>,
|
||||||
|
source: SampleRect,
|
||||||
|
alpha: Option<f32>,
|
||||||
|
color_description: Rc<ColorDescription>,
|
||||||
|
render_intent: RenderIntent,
|
||||||
|
alpha_mode: crate::gfx_api::AlphaMode,
|
||||||
|
opaque: bool,
|
||||||
|
},
|
||||||
|
Color {
|
||||||
|
color: Color,
|
||||||
|
alpha: Option<f32>,
|
||||||
|
color_description: Rc<ColorDescription>,
|
||||||
|
render_intent: RenderIntent,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RetainedToplevel {
|
||||||
|
pub fn capture_surface(surface: &WlSurface, offset: (i32, i32)) -> Option<Rc<Self>> {
|
||||||
|
Some(Rc::new(Self {
|
||||||
|
offset,
|
||||||
|
surface: RetainedSurface::capture(surface, (0, 0))?,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RetainedSurface {
|
||||||
|
fn capture(surface: &WlSurface, offset: (i32, i32)) -> Option<Self> {
|
||||||
|
let buffer = surface.buffer.get()?;
|
||||||
|
let size = surface.buffer_abs_pos.get().size();
|
||||||
|
let source = *surface.buffer_points_norm.borrow();
|
||||||
|
let color_description = surface.color_description();
|
||||||
|
let render_intent = surface.render_intent();
|
||||||
|
let alpha_mode = surface.alpha_mode();
|
||||||
|
let alpha = surface.alpha();
|
||||||
|
let content = match buffer.buffer.buf.get_stable_texture() {
|
||||||
|
Some(texture) => RetainedContent::Texture {
|
||||||
|
opaque: surface.opaque(),
|
||||||
|
texture,
|
||||||
|
buffer,
|
||||||
|
source,
|
||||||
|
alpha,
|
||||||
|
color_description,
|
||||||
|
render_intent,
|
||||||
|
alpha_mode,
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let color = buffer.buffer.buf.color?;
|
||||||
|
RetainedContent::Color {
|
||||||
|
color: Color::from_u32(
|
||||||
|
color_description.eotf,
|
||||||
|
alpha_mode,
|
||||||
|
color[0],
|
||||||
|
color[1],
|
||||||
|
color[2],
|
||||||
|
color[3],
|
||||||
|
),
|
||||||
|
alpha,
|
||||||
|
color_description,
|
||||||
|
render_intent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut below = vec![];
|
||||||
|
let mut above = vec![];
|
||||||
|
if let Some(children) = surface.children.borrow().as_deref() {
|
||||||
|
for child in children.below.iter() {
|
||||||
|
if child.pending.get() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let pos = child.sub_surface.position.get();
|
||||||
|
below.push(Self::capture(&child.sub_surface.surface, pos)?);
|
||||||
|
}
|
||||||
|
for child in children.above.iter() {
|
||||||
|
if child.pending.get() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let pos = child.sub_surface.position.get();
|
||||||
|
above.push(Self::capture(&child.sub_surface.surface, pos)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Self {
|
||||||
|
offset,
|
||||||
|
size,
|
||||||
|
content,
|
||||||
|
below,
|
||||||
|
above,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for AnimationState {
|
impl Default for AnimationState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -79,6 +189,7 @@ impl AnimationState {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
old: Rect,
|
old: Rect,
|
||||||
new: Rect,
|
new: Rect,
|
||||||
|
retained: Option<Rc<RetainedToplevel>>,
|
||||||
now_nsec: u64,
|
now_nsec: u64,
|
||||||
duration_ms: u32,
|
duration_ms: u32,
|
||||||
curve: AnimationCurve,
|
curve: AnimationCurve,
|
||||||
|
|
@ -89,10 +200,10 @@ impl AnimationState {
|
||||||
}
|
}
|
||||||
let duration_nsec = duration_ms as u64 * 1_000_000;
|
let duration_nsec = duration_ms as u64 * 1_000_000;
|
||||||
let mut windows = self.windows.borrow_mut();
|
let mut windows = self.windows.borrow_mut();
|
||||||
let from = match windows.get(&node_id) {
|
let (from, retained) = match windows.get(&node_id) {
|
||||||
Some(anim) if anim.to == new => return false,
|
Some(anim) if anim.to == new => return false,
|
||||||
Some(anim) => anim.rect_at(now_nsec),
|
Some(anim) => (anim.rect_at(now_nsec), anim.retained.clone().or(retained)),
|
||||||
None => old,
|
None => (old, retained),
|
||||||
};
|
};
|
||||||
if from == new {
|
if from == new {
|
||||||
windows.remove(&node_id);
|
windows.remove(&node_id);
|
||||||
|
|
@ -107,6 +218,7 @@ impl AnimationState {
|
||||||
duration_nsec,
|
duration_nsec,
|
||||||
curve,
|
curve,
|
||||||
last_damage: from,
|
last_damage: from,
|
||||||
|
retained,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
true
|
true
|
||||||
|
|
@ -120,6 +232,18 @@ impl AnimationState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn retained_snapshot(
|
||||||
|
&self,
|
||||||
|
node_id: NodeId,
|
||||||
|
now_nsec: u64,
|
||||||
|
) -> Option<Rc<RetainedToplevel>> {
|
||||||
|
let windows = self.windows.borrow();
|
||||||
|
match windows.get(&node_id) {
|
||||||
|
Some(anim) if !anim.done(now_nsec) => anim.retained.clone(),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn damage_active(&self, state: &State, now_nsec: u64) -> bool {
|
fn damage_active(&self, state: &State, now_nsec: u64) -> bool {
|
||||||
let mut damages = vec![];
|
let mut damages = vec![];
|
||||||
let mut any_active = false;
|
let mut any_active = false;
|
||||||
|
|
@ -164,6 +288,7 @@ struct WindowAnimation {
|
||||||
duration_nsec: u64,
|
duration_nsec: u64,
|
||||||
curve: AnimationCurve,
|
curve: AnimationCurve,
|
||||||
last_damage: Rect,
|
last_damage: Rect,
|
||||||
|
retained: Option<Rc<RetainedToplevel>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowAnimation {
|
impl WindowAnimation {
|
||||||
|
|
@ -286,8 +411,8 @@ mod tests {
|
||||||
let id = NodeId(1);
|
let id = NodeId(1);
|
||||||
let a = Rect::new_sized_saturating(0, 0, 100, 100);
|
let a = Rect::new_sized_saturating(0, 0, 100, 100);
|
||||||
let b = Rect::new_sized_saturating(100, 0, 100, 100);
|
let b = Rect::new_sized_saturating(100, 0, 100, 100);
|
||||||
assert!(state.set_target(id, a, b, 0, 160, AnimationCurve::Linear));
|
assert!(state.set_target(id, a, b, None, 0, 160, AnimationCurve::Linear));
|
||||||
assert!(!state.set_target(id, a, b, 80_000_000, 160, AnimationCurve::Linear));
|
assert!(!state.set_target(id, a, b, None, 80_000_000, 160, AnimationCurve::Linear));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
state.visual_rect(id, b, 80_000_000),
|
state.visual_rect(id, b, 80_000_000),
|
||||||
Rect::new_sized_saturating(50, 0, 100, 100)
|
Rect::new_sized_saturating(50, 0, 100, 100)
|
||||||
|
|
@ -301,8 +426,8 @@ mod tests {
|
||||||
let a = Rect::new_sized_saturating(0, 0, 100, 100);
|
let a = Rect::new_sized_saturating(0, 0, 100, 100);
|
||||||
let b = Rect::new_sized_saturating(100, 0, 100, 100);
|
let b = Rect::new_sized_saturating(100, 0, 100, 100);
|
||||||
let c = Rect::new_sized_saturating(200, 0, 100, 100);
|
let c = Rect::new_sized_saturating(200, 0, 100, 100);
|
||||||
assert!(state.set_target(id, a, b, 0, 160, AnimationCurve::Linear));
|
assert!(state.set_target(id, a, b, None, 0, 160, AnimationCurve::Linear));
|
||||||
assert!(state.set_target(id, a, c, 80_000_000, 160, AnimationCurve::Linear));
|
assert!(state.set_target(id, a, c, None, 80_000_000, 160, AnimationCurve::Linear));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
state.visual_rect(id, c, 80_000_000),
|
state.visual_rect(id, c, 80_000_000),
|
||||||
Rect::new_sized_saturating(50, 0, 100, 100)
|
Rect::new_sized_saturating(50, 0, 100, 100)
|
||||||
|
|
|
||||||
|
|
@ -310,6 +310,19 @@ impl WlBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_stable_texture(&self) -> Option<Rc<dyn GfxTexture>> {
|
||||||
|
match &*self.storage.borrow() {
|
||||||
|
None => None,
|
||||||
|
Some(s) => match s {
|
||||||
|
WlBufferStorage::Shm {
|
||||||
|
dmabuf_buffer_params,
|
||||||
|
..
|
||||||
|
} => dmabuf_buffer_params.tex.clone(),
|
||||||
|
WlBufferStorage::Dmabuf { tex, .. } => tex.clone(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_texture_or_log(&self, surface: &WlSurface, sync_shm: bool) {
|
pub fn update_texture_or_log(&self, surface: &WlSurface, sync_shm: bool) {
|
||||||
if let Err(e) = self.update_texture(surface, sync_shm) {
|
if let Err(e) = self.update_texture(surface, sync_shm) {
|
||||||
log::warn!("Could not update texture: {}", ErrorFmt(e));
|
log::warn!("Could not update texture: {}", ErrorFmt(e));
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
animation::RetainedToplevel,
|
||||||
client::Client,
|
client::Client,
|
||||||
cursor::KnownCursor,
|
cursor::KnownCursor,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
|
|
@ -514,6 +515,10 @@ impl ToplevelNodeBase for Xwindow {
|
||||||
Some(self.x.surface.clone())
|
Some(self.x.surface.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tl_animation_snapshot(&self) -> Option<Rc<RetainedToplevel>> {
|
||||||
|
RetainedToplevel::capture_surface(&self.x.surface, (0, 0))
|
||||||
|
}
|
||||||
|
|
||||||
fn tl_admits_children(&self) -> bool {
|
fn tl_admits_children(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ pub mod xdg_dialog_v1;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
animation::RetainedToplevel,
|
||||||
bugs,
|
bugs,
|
||||||
bugs::Bugs,
|
bugs::Bugs,
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
|
|
@ -779,6 +780,11 @@ impl ToplevelNodeBase for XdgToplevel {
|
||||||
Some(self.xdg.surface.clone())
|
Some(self.xdg.surface.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tl_animation_snapshot(&self) -> Option<Rc<RetainedToplevel>> {
|
||||||
|
let geo = self.xdg.geometry();
|
||||||
|
RetainedToplevel::capture_surface(&self.xdg.surface, (-geo.x1(), -geo.y1()))
|
||||||
|
}
|
||||||
|
|
||||||
fn tl_restack_popups(&self) {
|
fn tl_restack_popups(&self) {
|
||||||
self.xdg.restack_popups();
|
self.xdg.restack_popups();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
182
src/renderer.rs
182
src/renderer.rs
|
|
@ -1,7 +1,8 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
animation::{RetainedContent, RetainedSurface, RetainedToplevel},
|
||||||
cmm::cmm_render_intent::RenderIntent,
|
cmm::cmm_render_intent::RenderIntent,
|
||||||
gfx_api::{AcquireSync, AlphaMode, GfxApiOpt, ReleaseSync, SampleRect},
|
gfx_api::{AcquireSync, AlphaMode, BufferResv, GfxApiOpt, ReleaseSync, SampleRect},
|
||||||
ifs::wl_surface::{
|
ifs::wl_surface::{
|
||||||
SurfaceBuffer, WlSurface,
|
SurfaceBuffer, WlSurface,
|
||||||
x_surface::xwindow::Xwindow,
|
x_surface::xwindow::Xwindow,
|
||||||
|
|
@ -467,6 +468,167 @@ impl Renderer<'_> {
|
||||||
visual.move_(-container.abs_x1.get(), -container.abs_y1.get())
|
visual.move_(-container.abs_x1.get(), -container.abs_y1.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_child_or_snapshot(
|
||||||
|
&mut self,
|
||||||
|
child: &Rc<dyn ToplevelNode>,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
bounds: Option<&Rect>,
|
||||||
|
) {
|
||||||
|
if let Some(retained) = self
|
||||||
|
.state
|
||||||
|
.animations
|
||||||
|
.retained_snapshot(child.node_id(), self.state.now_nsec())
|
||||||
|
{
|
||||||
|
self.render_retained_toplevel(&retained, x, y, bounds);
|
||||||
|
} else {
|
||||||
|
child.node_render(self, x, y, bounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_retained_toplevel(
|
||||||
|
&mut self,
|
||||||
|
retained: &RetainedToplevel,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
bounds: Option<&Rect>,
|
||||||
|
) {
|
||||||
|
let (x, y) = self
|
||||||
|
.base
|
||||||
|
.scale_point(x + retained.offset.0, y + retained.offset.1);
|
||||||
|
self.render_retained_surface_scaled(&retained.surface, x, y, None, bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_retained_surface_scaled(
|
||||||
|
&mut self,
|
||||||
|
retained: &RetainedSurface,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
pos_rel: Option<(i32, i32)>,
|
||||||
|
bounds: Option<&Rect>,
|
||||||
|
) {
|
||||||
|
let stretch = self.stretch.take();
|
||||||
|
let corner_radius = self.corner_radius.take();
|
||||||
|
let mut size = retained.size;
|
||||||
|
if let Some((x_rel, y_rel)) = pos_rel {
|
||||||
|
let (x, y) = self.base.scale_point(x_rel, y_rel);
|
||||||
|
let (w, h) = self.base.scale_point(x_rel + size.0, y_rel + size.1);
|
||||||
|
size = (w - x, h - y);
|
||||||
|
} else {
|
||||||
|
size = self.base.scale_point(size.0, size.1);
|
||||||
|
}
|
||||||
|
let mut stretched_source = None;
|
||||||
|
if let Some(s) = stretch {
|
||||||
|
if let RetainedContent::Texture { source, .. } = &retained.content {
|
||||||
|
let mut source = *source;
|
||||||
|
if size.0 > 0 && size.1 > 0 {
|
||||||
|
let sx = s.0 as f32 / size.0 as f32;
|
||||||
|
let sy = s.1 as f32 / size.1 as f32;
|
||||||
|
source.x2 *= sx;
|
||||||
|
source.y2 *= sy;
|
||||||
|
}
|
||||||
|
stretched_source = Some(source);
|
||||||
|
}
|
||||||
|
size = s;
|
||||||
|
}
|
||||||
|
for child in &retained.below {
|
||||||
|
let (x1, y1) = self.base.scale_point(child.offset.0, child.offset.1);
|
||||||
|
self.render_retained_surface_scaled(child, x + x1, y + y1, Some(child.offset), bounds);
|
||||||
|
}
|
||||||
|
self.corner_radius = corner_radius;
|
||||||
|
self.render_retained_content(retained, stretched_source, x, y, size, bounds);
|
||||||
|
for child in &retained.above {
|
||||||
|
let (x1, y1) = self.base.scale_point(child.offset.0, child.offset.1);
|
||||||
|
self.render_retained_surface_scaled(child, x + x1, y + y1, Some(child.offset), bounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_retained_content(
|
||||||
|
&mut self,
|
||||||
|
retained: &RetainedSurface,
|
||||||
|
stretched_source: Option<SampleRect>,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
size: (i32, i32),
|
||||||
|
bounds: Option<&Rect>,
|
||||||
|
) {
|
||||||
|
let corner_radius = self.corner_radius.take();
|
||||||
|
match &retained.content {
|
||||||
|
RetainedContent::Texture {
|
||||||
|
texture,
|
||||||
|
buffer,
|
||||||
|
source,
|
||||||
|
alpha,
|
||||||
|
color_description,
|
||||||
|
render_intent,
|
||||||
|
alpha_mode,
|
||||||
|
opaque,
|
||||||
|
} => {
|
||||||
|
let source = stretched_source.unwrap_or(*source);
|
||||||
|
if let Some(cr) = corner_radius {
|
||||||
|
self.base.render_rounded_texture(
|
||||||
|
texture,
|
||||||
|
*alpha,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
Some(source),
|
||||||
|
Some(size),
|
||||||
|
self.base.scale,
|
||||||
|
bounds,
|
||||||
|
Some(buffer.clone() as Rc<dyn BufferResv>),
|
||||||
|
AcquireSync::Unnecessary,
|
||||||
|
buffer.release_sync,
|
||||||
|
color_description,
|
||||||
|
*render_intent,
|
||||||
|
*alpha_mode,
|
||||||
|
cr,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.base.render_texture(
|
||||||
|
texture,
|
||||||
|
*alpha,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
Some(source),
|
||||||
|
Some(size),
|
||||||
|
self.base.scale,
|
||||||
|
bounds,
|
||||||
|
Some(buffer.clone() as Rc<dyn BufferResv>),
|
||||||
|
AcquireSync::Unnecessary,
|
||||||
|
buffer.release_sync,
|
||||||
|
*opaque,
|
||||||
|
color_description,
|
||||||
|
*render_intent,
|
||||||
|
*alpha_mode,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RetainedContent::Color {
|
||||||
|
color,
|
||||||
|
alpha,
|
||||||
|
color_description,
|
||||||
|
render_intent,
|
||||||
|
} => {
|
||||||
|
if let Some(rect) = Rect::new_sized(x, y, size.0, size.1) {
|
||||||
|
let rect = match bounds {
|
||||||
|
None => rect,
|
||||||
|
Some(bounds) => rect.intersect(*bounds),
|
||||||
|
};
|
||||||
|
if !rect.is_empty() {
|
||||||
|
self.base.sync();
|
||||||
|
self.base.fill_scaled_boxes(
|
||||||
|
&[rect],
|
||||||
|
color,
|
||||||
|
*alpha,
|
||||||
|
&color_description.linear,
|
||||||
|
*render_intent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render_container(&mut self, container: &ContainerNode, x: i32, y: i32) {
|
pub fn render_container(&mut self, container: &ContainerNode, x: i32, y: i32) {
|
||||||
self.render_container_decorations(container, x, y);
|
self.render_container_decorations(container, x, y);
|
||||||
|
|
||||||
|
|
@ -526,9 +688,12 @@ impl Renderer<'_> {
|
||||||
self.corner_radius = Some(inner_cr);
|
self.corner_radius = Some(inner_cr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
child
|
self.render_child_or_snapshot(
|
||||||
.node
|
&child.node,
|
||||||
.node_render(self, x + content.x1(), y + content.y1(), Some(&body));
|
x + content.x1(),
|
||||||
|
y + content.y1(),
|
||||||
|
Some(&body),
|
||||||
|
);
|
||||||
self.stretch = None;
|
self.stretch = None;
|
||||||
self.corner_radius = None;
|
self.corner_radius = None;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -579,9 +744,12 @@ impl Renderer<'_> {
|
||||||
}
|
}
|
||||||
let body = body.move_(x, y);
|
let body = body.move_(x, y);
|
||||||
let body = self.base.scale_rect(body);
|
let body = self.base.scale_rect(body);
|
||||||
child
|
self.render_child_or_snapshot(
|
||||||
.node
|
&child.node,
|
||||||
.node_render(self, x + content.x1(), y + content.y1(), Some(&body));
|
x + content.x1(),
|
||||||
|
y + content.y1(),
|
||||||
|
Some(&body),
|
||||||
|
);
|
||||||
self.stretch = None;
|
self.stretch = None;
|
||||||
self.corner_radius = None;
|
self.corner_radius = None;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
src/state.rs
13
src/state.rs
|
|
@ -2,7 +2,9 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
acceptor::Acceptor,
|
acceptor::Acceptor,
|
||||||
allocator::BufferObject,
|
allocator::BufferObject,
|
||||||
animation::{AnimationCurve, AnimationState, AnimationTick, expand_damage_rect},
|
animation::{
|
||||||
|
AnimationCurve, AnimationState, AnimationTick, RetainedToplevel, expand_damage_rect,
|
||||||
|
},
|
||||||
async_engine::{AsyncEngine, SpawnedFuture},
|
async_engine::{AsyncEngine, SpawnedFuture},
|
||||||
backend::{
|
backend::{
|
||||||
Backend, BackendConnectorState, BackendConnectorStateSerials, BackendDrmDevice,
|
Backend, BackendConnectorState, BackendConnectorStateSerials, BackendDrmDevice,
|
||||||
|
|
@ -1469,7 +1471,13 @@ impl State {
|
||||||
self.eng.now().msec()
|
self.eng.now().msec()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn queue_tiled_animation(self: &Rc<Self>, node_id: NodeId, old: Rect, new: Rect) {
|
pub fn queue_tiled_animation(
|
||||||
|
self: &Rc<Self>,
|
||||||
|
node_id: NodeId,
|
||||||
|
old: Rect,
|
||||||
|
new: Rect,
|
||||||
|
retained: Option<Rc<RetainedToplevel>>,
|
||||||
|
) {
|
||||||
if !self.animations.enabled.get()
|
if !self.animations.enabled.get()
|
||||||
|| !self.layout_animations_active.get()
|
|| !self.layout_animations_active.get()
|
||||||
|| self.suppress_animations_for_next_layout.get()
|
|| self.suppress_animations_for_next_layout.get()
|
||||||
|
|
@ -1494,6 +1502,7 @@ impl State {
|
||||||
node_id,
|
node_id,
|
||||||
old,
|
old,
|
||||||
new,
|
new,
|
||||||
|
retained,
|
||||||
now,
|
now,
|
||||||
self.animations.duration_ms.get(),
|
self.animations.duration_ms.get(),
|
||||||
self.animations.curve.get(),
|
self.animations.curve.get(),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
animation::RetainedToplevel,
|
||||||
client::{Client, ClientId},
|
client::{Client, ClientId},
|
||||||
criteria::{
|
criteria::{
|
||||||
CritDestroyListener, CritMatcherId,
|
CritDestroyListener, CritMatcherId,
|
||||||
|
|
@ -197,9 +198,12 @@ impl<T: ToplevelNodeBase> ToplevelNode for T {
|
||||||
&& !self.node_is_container()
|
&& !self.node_is_container()
|
||||||
&& !parent_is_mono
|
&& !parent_is_mono
|
||||||
{
|
{
|
||||||
data.state
|
data.state.clone().queue_tiled_animation(
|
||||||
.clone()
|
data.node_id,
|
||||||
.queue_tiled_animation(data.node_id, prev, *rect);
|
prev,
|
||||||
|
*rect,
|
||||||
|
self.tl_animation_snapshot(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if prev.size() != rect.size() {
|
if prev.size() != rect.size() {
|
||||||
for sc in data.jay_screencasts.lock().values() {
|
for sc in data.jay_screencasts.lock().values() {
|
||||||
|
|
@ -316,6 +320,11 @@ pub trait ToplevelNodeBase: Node {
|
||||||
fn tl_scanout_surface(&self) -> Option<Rc<WlSurface>> {
|
fn tl_scanout_surface(&self) -> Option<Rc<WlSurface>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tl_animation_snapshot(&self) -> Option<Rc<RetainedToplevel>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn tl_restack_popups(&self) {
|
fn tl_restack_popups(&self) {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue