Repair animation integration paths
This commit is contained in:
parent
31c289f628
commit
0fefe814c3
9 changed files with 229 additions and 35 deletions
|
|
@ -195,13 +195,14 @@ impl RetainedToplevel {
|
|||
impl RetainedSurface {
|
||||
fn capture(surface: &WlSurface, offset: (i32, i32)) -> Option<Self> {
|
||||
let buffer = surface.buffer.get()?;
|
||||
buffer.buffer.buf.update_texture_or_log(surface, true);
|
||||
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() {
|
||||
let content = match buffer.buffer.buf.get_texture(surface) {
|
||||
Some(texture) => RetainedContent::Texture {
|
||||
opaque: surface.opaque(),
|
||||
texture,
|
||||
|
|
@ -237,14 +238,18 @@ impl RetainedSurface {
|
|||
continue;
|
||||
}
|
||||
let pos = child.sub_surface.position.get();
|
||||
below.push(Self::capture(&child.sub_surface.surface, pos)?);
|
||||
if let Some(surface) = Self::capture(&child.sub_surface.surface, pos) {
|
||||
below.push(surface);
|
||||
}
|
||||
}
|
||||
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)?);
|
||||
if let Some(surface) = Self::capture(&child.sub_surface.surface, pos) {
|
||||
above.push(surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(Self {
|
||||
|
|
|
|||
|
|
@ -152,6 +152,7 @@ pub enum PlanStrategy {
|
|||
SingleAction,
|
||||
MixedSinglePhase,
|
||||
HierarchyOrderedScales,
|
||||
OrientationChange { from_axis: PhaseAxis },
|
||||
SwapLanes { axis: PhaseAxis },
|
||||
SpaceThenOrthogonalGrowth { axis: PhaseAxis },
|
||||
ReversedForwardPlan { original: Box<PlanStrategy> },
|
||||
|
|
@ -501,6 +502,22 @@ fn plan_forward(
|
|||
}
|
||||
}
|
||||
}
|
||||
for axis in [PhaseAxis::Horizontal, PhaseAxis::Vertical] {
|
||||
match plan_orientation_change(request, axis) {
|
||||
Ok(plan) => return Ok(plan),
|
||||
Err(error) => {
|
||||
record_rejection(
|
||||
&mut attempted,
|
||||
direction,
|
||||
PlanStrategy::OrientationChange { from_axis: axis },
|
||||
error,
|
||||
);
|
||||
if error != MultiphasePlanFailure::NoPattern {
|
||||
rejection.get_or_insert(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for axis in [PhaseAxis::Horizontal, PhaseAxis::Vertical] {
|
||||
match plan_axis_crossing_lanes(request, axis) {
|
||||
Ok(plan) => return Ok(plan),
|
||||
|
|
@ -920,6 +937,94 @@ fn plan_space_then_orthogonal_growth(
|
|||
)
|
||||
}
|
||||
|
||||
fn plan_orientation_change(
|
||||
request: &MultiphaseRequest,
|
||||
from_axis: PhaseAxis,
|
||||
) -> Result<MultiphasePlanned, MultiphasePlanFailure> {
|
||||
if request.windows.len() < 2 {
|
||||
return Err(MultiphasePlanFailure::NoPattern);
|
||||
}
|
||||
let to_axis = from_axis.other();
|
||||
let min_lane_size = sane_min_size(main_size(request.bounds, to_axis));
|
||||
let target_start = request
|
||||
.windows
|
||||
.first()
|
||||
.map(|window| main_start(window.to, from_axis))
|
||||
.ok_or(MultiphasePlanFailure::NoPattern)?;
|
||||
let target_end = request
|
||||
.windows
|
||||
.first()
|
||||
.map(|window| main_end(window.to, from_axis))
|
||||
.ok_or(MultiphasePlanFailure::NoPattern)?;
|
||||
let source_start = request
|
||||
.windows
|
||||
.first()
|
||||
.map(|window| main_start(window.from, to_axis))
|
||||
.ok_or(MultiphasePlanFailure::NoPattern)?;
|
||||
let source_end = request
|
||||
.windows
|
||||
.first()
|
||||
.map(|window| main_end(window.from, to_axis))
|
||||
.ok_or(MultiphasePlanFailure::NoPattern)?;
|
||||
if request.windows.iter().any(|window| {
|
||||
main_start(window.from, to_axis) != source_start
|
||||
|| main_end(window.from, to_axis) != source_end
|
||||
|| main_start(window.to, from_axis) != target_start
|
||||
|| main_end(window.to, from_axis) != target_end
|
||||
|| main_size(window.to, to_axis) < min_lane_size
|
||||
}) {
|
||||
return Err(MultiphasePlanFailure::NoPattern);
|
||||
}
|
||||
|
||||
let mut phase1 = vec![];
|
||||
let mut phase2 = vec![];
|
||||
let mut phase3 = vec![];
|
||||
for window in &request.windows {
|
||||
let lane = with_main_interval(
|
||||
window.from,
|
||||
to_axis,
|
||||
main_start(window.to, to_axis),
|
||||
main_end(window.to, to_axis),
|
||||
);
|
||||
let moved = with_main_interval(
|
||||
lane,
|
||||
from_axis,
|
||||
main_start(window.to, from_axis),
|
||||
main_start(window.to, from_axis) + main_size(lane, from_axis),
|
||||
);
|
||||
push_step(&mut phase1, window.node_id, window.from, lane);
|
||||
push_step(&mut phase2, window.node_id, lane, moved);
|
||||
push_step(&mut phase3, window.node_id, moved, window.to);
|
||||
}
|
||||
if phase1.is_empty() || phase3.is_empty() {
|
||||
return Err(MultiphasePlanFailure::NoPattern);
|
||||
}
|
||||
build_validated_plan(
|
||||
request,
|
||||
PlanStrategy::OrientationChange { from_axis },
|
||||
[
|
||||
phase_draft(
|
||||
PhaseKind::Scale,
|
||||
to_axis,
|
||||
phase1,
|
||||
PhaseReason::ShrinkIntoLanes { lane_axis: to_axis },
|
||||
),
|
||||
phase_draft(
|
||||
PhaseKind::Move,
|
||||
from_axis,
|
||||
phase2,
|
||||
PhaseReason::MoveThroughFreedSpace,
|
||||
),
|
||||
phase_draft(
|
||||
PhaseKind::Scale,
|
||||
from_axis,
|
||||
phase3,
|
||||
PhaseReason::GrowOutOfLanes,
|
||||
),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
struct MultiphasePhaseDraft {
|
||||
action: MultiphasePhaseActionDraft,
|
||||
steps: Vec<MultiphaseStep>,
|
||||
|
|
@ -1665,6 +1770,20 @@ mod tests {
|
|||
strategy: PlanStrategy::HierarchyOrderedScales,
|
||||
reason: MultiphasePlanFailure::NoPattern,
|
||||
},
|
||||
RejectedStrategy {
|
||||
direction,
|
||||
strategy: PlanStrategy::OrientationChange {
|
||||
from_axis: PhaseAxis::Horizontal,
|
||||
},
|
||||
reason: MultiphasePlanFailure::NoPattern,
|
||||
},
|
||||
RejectedStrategy {
|
||||
direction,
|
||||
strategy: PlanStrategy::OrientationChange {
|
||||
from_axis: PhaseAxis::Vertical,
|
||||
},
|
||||
reason: MultiphasePlanFailure::NoPattern,
|
||||
},
|
||||
RejectedStrategy {
|
||||
direction,
|
||||
strategy: PlanStrategy::SwapLanes {
|
||||
|
|
@ -2024,6 +2143,46 @@ mod tests {
|
|||
assert!(validate_plan_continuous(&req, plan));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn orientation_change_shrinks_moves_then_grows() {
|
||||
let req = request(vec![
|
||||
window(1, rect(0, 0, 200, 400), rect(0, 0, 400, 200)),
|
||||
window(2, rect(200, 0, 400, 400), rect(0, 200, 400, 400)),
|
||||
]);
|
||||
let planned = plan_no_overlap_explained(&req).unwrap();
|
||||
let plan = &planned.plan;
|
||||
|
||||
assert_eq!(
|
||||
planned.explanation.strategy,
|
||||
PlanStrategy::OrientationChange {
|
||||
from_axis: PhaseAxis::Horizontal,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
actions(plan),
|
||||
vec![
|
||||
PhaseAction {
|
||||
kind: PhaseKind::Scale,
|
||||
axis: PhaseAxis::Vertical,
|
||||
},
|
||||
PhaseAction {
|
||||
kind: PhaseKind::Move,
|
||||
axis: PhaseAxis::Horizontal,
|
||||
},
|
||||
PhaseAction {
|
||||
kind: PhaseKind::Scale,
|
||||
axis: PhaseAxis::Horizontal,
|
||||
},
|
||||
]
|
||||
);
|
||||
assert_eq!(step_to(plan, 0, id(1)), rect(0, 0, 200, 200));
|
||||
assert_eq!(step_to(plan, 0, id(2)), rect(200, 200, 400, 400));
|
||||
assert_eq!(step_to(plan, 1, id(2)), rect(0, 200, 200, 400));
|
||||
assert_eq!(step_to(plan, 2, id(1)), rect(0, 0, 400, 200));
|
||||
assert_eq!(step_to(plan, 2, id(2)), rect(0, 200, 400, 400));
|
||||
assert!(validate_plan_continuous(&req, plan));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_axis_redistribution_without_hierarchy_still_falls_back() {
|
||||
let req = request(vec![
|
||||
|
|
|
|||
|
|
@ -310,19 +310,6 @@ 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) {
|
||||
if let Err(e) = self.update_texture(surface, sync_shm) {
|
||||
log::warn!("Could not update texture: {}", ErrorFmt(e));
|
||||
|
|
|
|||
|
|
@ -628,6 +628,11 @@ fn schedule_async_upload(
|
|||
{
|
||||
back_tex_opt = None;
|
||||
}
|
||||
if let Some(back_tex) = &back_tex_opt
|
||||
&& Rc::strong_count(back_tex) > 1
|
||||
{
|
||||
back_tex_opt = None;
|
||||
}
|
||||
let damage_full = || {
|
||||
back.damage.clear();
|
||||
back.damage.damage(slice::from_ref(&buf.rect));
|
||||
|
|
|
|||
|
|
@ -836,7 +836,14 @@ impl State {
|
|||
|
||||
pub fn map_tiled(self: &Rc<Self>, node: Rc<dyn ToplevelNode>) {
|
||||
let seat = self.seat_queue.last();
|
||||
self.do_map_tiled(seat.as_deref(), node.clone());
|
||||
let animate_new_app_map = node.tl_data().parent.is_none()
|
||||
&& node.tl_data().kind.is_app_window()
|
||||
&& !node.tl_data().visible.get();
|
||||
if animate_new_app_map {
|
||||
self.with_layout_animations(|| self.do_map_tiled(seat.as_deref(), node.clone()));
|
||||
} else {
|
||||
self.do_map_tiled(seat.as_deref(), node.clone());
|
||||
}
|
||||
self.focus_after_map(node, seat.as_deref());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1766,17 +1766,38 @@ enum SeatOpKind {
|
|||
|
||||
pub async fn container_layout(state: Rc<State>) {
|
||||
loop {
|
||||
let container = state.pending_container_layout.pop().await;
|
||||
if container.layout_scheduled.get() {
|
||||
let first = state.pending_container_layout.pop().await;
|
||||
let mut containers = vec![first];
|
||||
while let Some(container) = state.pending_container_layout.try_pop() {
|
||||
containers.push(container);
|
||||
}
|
||||
let mut animated = vec![];
|
||||
let mut immediate = vec![];
|
||||
for container in containers {
|
||||
if !container.layout_scheduled.get() {
|
||||
continue;
|
||||
}
|
||||
let animate = container.animate_next_layout.replace(false)
|
||||
&& !state.suppress_animations_for_next_layout.get();
|
||||
let prev_active = state.layout_animations_active.replace(animate);
|
||||
if animate {
|
||||
state.begin_layout_animation_batch();
|
||||
animated.push(container);
|
||||
} else {
|
||||
immediate.push(container);
|
||||
}
|
||||
container.perform_layout();
|
||||
if animate {
|
||||
state.finish_layout_animation_batch();
|
||||
}
|
||||
if !animated.is_empty() {
|
||||
let prev_active = state.layout_animations_active.replace(true);
|
||||
state.begin_layout_animation_batch();
|
||||
for container in animated {
|
||||
container.perform_layout();
|
||||
}
|
||||
state.finish_layout_animation_batch();
|
||||
state.layout_animations_active.set(prev_active);
|
||||
}
|
||||
if !immediate.is_empty() {
|
||||
let prev_active = state.layout_animations_active.replace(false);
|
||||
for container in immediate {
|
||||
container.perform_layout();
|
||||
}
|
||||
state.layout_animations_active.set(prev_active);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ use {
|
|||
crate::{
|
||||
animation::{
|
||||
RetainedExitLayer, RetainedToplevel,
|
||||
multiphase::{MultiphaseHierarchyPosition, MultiphaseWindowHierarchy, PhaseAxis},
|
||||
multiphase::{
|
||||
MultiphaseHierarchyPosition, MultiphaseHierarchyTransition,
|
||||
MultiphaseWindowHierarchy, PhaseAxis,
|
||||
},
|
||||
},
|
||||
client::{Client, ClientId},
|
||||
criteria::{
|
||||
|
|
@ -195,18 +198,30 @@ impl<T: ToplevelNodeBase> ToplevelNode for T {
|
|||
target_hierarchy,
|
||||
);
|
||||
let spawn_in_pending = data.spawn_in_pending.get();
|
||||
let spawn_in_eligible = spawn_in_pending
|
||||
&& !rect.is_empty()
|
||||
&& data.visible.get()
|
||||
&& !data.is_fullscreen.get()
|
||||
&& data.kind.is_app_window()
|
||||
&& !self.node_is_container();
|
||||
let parent_is_mono = data
|
||||
.parent
|
||||
.get()
|
||||
.and_then(|parent| parent.node_into_container())
|
||||
.is_some_and(|container| container.mono_child.is_some());
|
||||
let active_mono_boundary = matches!(
|
||||
hierarchy.transition,
|
||||
MultiphaseHierarchyTransition::EnteringMono
|
||||
| MultiphaseHierarchyTransition::ExitingMono
|
||||
) && (hierarchy.source.mono_active
|
||||
|| hierarchy.target.mono_active);
|
||||
if prev != *rect
|
||||
&& !prev.is_empty()
|
||||
&& !rect.is_empty()
|
||||
&& data.visible.get()
|
||||
&& !data.parent_is_float.get()
|
||||
&& !self.node_is_container()
|
||||
&& !parent_is_mono
|
||||
&& (!parent_is_mono || active_mono_boundary)
|
||||
{
|
||||
data.state.clone().queue_tiled_animation_with_hierarchy(
|
||||
data.node_id,
|
||||
|
|
@ -216,20 +231,14 @@ impl<T: ToplevelNodeBase> ToplevelNode for T {
|
|||
hierarchy,
|
||||
);
|
||||
}
|
||||
if spawn_in_pending
|
||||
&& !rect.is_empty()
|
||||
&& data.visible.get()
|
||||
&& !data.is_fullscreen.get()
|
||||
&& data.kind.is_app_window()
|
||||
&& !self.node_is_container()
|
||||
{
|
||||
if spawn_in_eligible {
|
||||
data.state.clone().queue_spawn_in_animation(
|
||||
data.node_id,
|
||||
*rect,
|
||||
self.tl_animation_snapshot(),
|
||||
);
|
||||
}
|
||||
if spawn_in_pending && !rect.is_empty() {
|
||||
if spawn_in_eligible {
|
||||
data.spawn_in_pending.set(false);
|
||||
}
|
||||
if prev.size() != rect.size() {
|
||||
|
|
|
|||
|
|
@ -197,10 +197,10 @@ impl WorkspaceNode {
|
|||
}
|
||||
self.pull_child_properties(&**container);
|
||||
let pos = self.position.get();
|
||||
container.clone().tl_change_extents(&pos);
|
||||
container.tl_set_parent(self.clone());
|
||||
container.tl_set_visible(self.container_visible());
|
||||
self.container.set(Some(container.clone()));
|
||||
container.clone().tl_change_extents(&pos);
|
||||
self.state.damage(self.position.get());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2034,6 +2034,7 @@ impl Wm {
|
|||
self.windows_by_surface_serial.remove(&serial);
|
||||
}
|
||||
if let Some(window) = data.window.take() {
|
||||
window.queue_spawn_out();
|
||||
window.destroy();
|
||||
}
|
||||
if let Some(parent) = data.parent.take() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue