feat: animate compositor geometry targets
This commit is contained in:
1 parent
7f847b2c0c
commit
b111cbf531
5 files changed
+102
-15
No files matched your search
Generated
+1
@@ -368,6 +368,7 @@ checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
|
|||||||
name = "ferese"
|
name = "ferese"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"ferese-animation",
|
||||||
"ferese-core",
|
"ferese-core",
|
||||||
"ferese-layout",
|
"ferese-layout",
|
||||||
"libc",
|
"libc",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ license.workspace = true
|
|||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
ferese-animation = { path = "../crates/ferese-animation" }
|
||||||
ferese-core = { path = "../crates/ferese-core" }
|
ferese-core = { path = "../crates/ferese-core" }
|
||||||
ferese-layout = { path = "../crates/ferese-layout" }
|
ferese-layout = { path = "../crates/ferese-layout" }
|
||||||
libc.workspace = true
|
libc.workspace = true
|
||||||
|
|||||||
@@ -36,12 +36,20 @@ impl CompositorHandler for Ferese {
|
|||||||
while let Some(parent) = get_parent(&root) {
|
while let Some(parent) = get_parent(&root) {
|
||||||
root = parent;
|
root = parent;
|
||||||
}
|
}
|
||||||
if let Some(window) = self.space.elements().find(|window| {
|
let window = {
|
||||||
|
self.space
|
||||||
|
.elements()
|
||||||
|
.find(|window| {
|
||||||
window
|
window
|
||||||
.toplevel()
|
.toplevel()
|
||||||
.is_some_and(|toplevel| toplevel.wl_surface() == &root)
|
.is_some_and(|toplevel| toplevel.wl_surface() == &root)
|
||||||
}) {
|
})
|
||||||
|
.cloned()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(window) = window {
|
||||||
window.on_commit();
|
window.on_commit();
|
||||||
|
self.record_client_commit(&window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xdg_shell::handle_commit(&mut self.popups, &mut self.space, surface);
|
xdg_shell::handle_commit(&mut self.popups, &mut self.space, surface);
|
||||||
|
|||||||
+85
-12
@@ -6,6 +6,7 @@ use std::{
|
|||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use ferese_animation::{ClientSize, SpringConfig, WindowGeometry};
|
||||||
use ferese_core::{WindowPlacement, WorkspaceSet};
|
use ferese_core::{WindowPlacement, WorkspaceSet};
|
||||||
use ferese_layout::{Axis, Direction, GapConfig, LayoutResult, Rect, SizeConstraints, WindowId};
|
use ferese_layout::{Axis, Direction, GapConfig, LayoutResult, Rect, SizeConstraints, WindowId};
|
||||||
|
|
||||||
@@ -47,9 +48,11 @@ pub struct Ferese {
|
|||||||
pub space: Space<Window>,
|
pub space: Space<Window>,
|
||||||
pub workspaces: WorkspaceSet,
|
pub workspaces: WorkspaceSet,
|
||||||
pub window_ids: HashMap<Window, WindowId>,
|
pub window_ids: HashMap<Window, WindowId>,
|
||||||
|
pub window_geometry: HashMap<WindowId, WindowGeometry>,
|
||||||
pub focused_window: Option<WindowId>,
|
pub focused_window: Option<WindowId>,
|
||||||
pub intercepted_keys: HashSet<smithay::input::keyboard::Keycode>,
|
pub intercepted_keys: HashSet<smithay::input::keyboard::Keycode>,
|
||||||
next_window_id: u64,
|
next_window_id: u64,
|
||||||
|
last_animation_tick: Instant,
|
||||||
pub popups: PopupManager,
|
pub popups: PopupManager,
|
||||||
pub seat: Seat<Self>,
|
pub seat: Seat<Self>,
|
||||||
pub compositor_state: CompositorState,
|
pub compositor_state: CompositorState,
|
||||||
@@ -94,17 +97,21 @@ impl Ferese {
|
|||||||
seat.add_pointer();
|
seat.add_pointer();
|
||||||
let socket_name = Self::init_wayland_listener(display, event_loop)?;
|
let socket_name = Self::init_wayland_listener(display, event_loop)?;
|
||||||
|
|
||||||
|
let start_time = Instant::now();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
start_time: Instant::now(),
|
start_time,
|
||||||
socket_name,
|
socket_name,
|
||||||
display_handle,
|
display_handle,
|
||||||
loop_signal: event_loop.get_signal(),
|
loop_signal: event_loop.get_signal(),
|
||||||
space: Space::default(),
|
space: Space::default(),
|
||||||
workspaces: WorkspaceSet::default(),
|
workspaces: WorkspaceSet::default(),
|
||||||
window_ids: HashMap::new(),
|
window_ids: HashMap::new(),
|
||||||
|
window_geometry: HashMap::new(),
|
||||||
focused_window: None,
|
focused_window: None,
|
||||||
intercepted_keys: HashSet::new(),
|
intercepted_keys: HashSet::new(),
|
||||||
next_window_id: 1,
|
next_window_id: 1,
|
||||||
|
last_animation_tick: start_time,
|
||||||
popups: PopupManager::default(),
|
popups: PopupManager::default(),
|
||||||
seat,
|
seat,
|
||||||
compositor_state,
|
compositor_state,
|
||||||
@@ -228,6 +235,7 @@ impl Ferese {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.space.unmap_elem(window);
|
self.space.unmap_elem(window);
|
||||||
|
self.window_geometry.remove(&id);
|
||||||
if let Err(error) = self.workspaces.remove_window(id) {
|
if let Err(error) = self.workspaces.remove_window(id) {
|
||||||
tracing::error!(%error, ?id, "failed to remove window from layout");
|
tracing::error!(%error, ?id, "failed to remove window from layout");
|
||||||
}
|
}
|
||||||
@@ -298,32 +306,88 @@ impl Ferese {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Some((window.clone(), rect, fullscreen == Some(*id)))
|
Some((window.clone(), *id, rect, fullscreen == Some(*id)))
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for (window, rect, is_fullscreen) in placements {
|
let now = self.start_time.elapsed();
|
||||||
let location = (rect.x.round() as i32, rect.y.round() as i32);
|
|
||||||
let size = (
|
for (window, id, rect, is_fullscreen) in placements {
|
||||||
(rect.width.round() as i32).max(1),
|
let committed_size = client_size(&window);
|
||||||
(rect.height.round() as i32).max(1),
|
let geometry = self
|
||||||
);
|
.window_geometry
|
||||||
|
.entry(id)
|
||||||
|
.or_insert_with(|| WindowGeometry::new(rect, committed_size));
|
||||||
|
let requested_size = geometry.set_logical_target(rect, now);
|
||||||
|
let visual = geometry.visual.current;
|
||||||
|
let location = (visual.x.round() as i32, visual.y.round() as i32);
|
||||||
|
|
||||||
self.space.map_element(window.clone(), location, false);
|
self.space.map_element(window.clone(), location, false);
|
||||||
if let Some(toplevel) = window.toplevel() {
|
if let Some(toplevel) = window.toplevel() {
|
||||||
toplevel.with_pending_state(|state| {
|
let state_changed = toplevel.with_pending_state(|state| {
|
||||||
state.size = Some(size.into());
|
if let Some(size) = requested_size {
|
||||||
|
state.size = Some((size.width, size.height).into());
|
||||||
|
}
|
||||||
|
|
||||||
if is_fullscreen {
|
if is_fullscreen {
|
||||||
state.states.set(xdg_toplevel::State::Fullscreen);
|
state.states.set(xdg_toplevel::State::Fullscreen)
|
||||||
} else {
|
} else {
|
||||||
state.states.unset(xdg_toplevel::State::Fullscreen);
|
state.states.unset(xdg_toplevel::State::Fullscreen)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if requested_size.is_some() || state_changed {
|
||||||
toplevel.send_pending_configure();
|
toplevel.send_pending_configure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn advance_animations(&mut self, now: Instant) -> bool {
|
||||||
|
let delta = now.saturating_duration_since(self.last_animation_tick);
|
||||||
|
self.last_animation_tick = now;
|
||||||
|
let windows = self
|
||||||
|
.space
|
||||||
|
.elements()
|
||||||
|
.filter_map(|window| {
|
||||||
|
self.window_ids
|
||||||
|
.get(window)
|
||||||
|
.copied()
|
||||||
|
.map(|id| (window.clone(), id))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let mut active_animation = false;
|
||||||
|
|
||||||
|
for (window, id) in windows {
|
||||||
|
let Some(geometry) = self.window_geometry.get_mut(&id) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
active_animation |= geometry.advance(delta, SpringConfig::default(), true);
|
||||||
|
let visual = geometry.visual.current;
|
||||||
|
self.space.map_element(
|
||||||
|
window,
|
||||||
|
(visual.x.round() as i32, visual.y.round() as i32),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
active_animation
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn record_client_commit(&mut self, window: &Window) {
|
||||||
|
let Some(id) = self.window_ids.get(window) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(size) = client_size(window) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(geometry) = self.window_geometry.get_mut(id) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
geometry.client.commit(size);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn focus_direction(&mut self, direction: Direction) {
|
pub fn focus_direction(&mut self, direction: Direction) {
|
||||||
let Some(current) = self.focused_window else {
|
let Some(current) = self.focused_window else {
|
||||||
@@ -655,6 +719,15 @@ fn centered_transient_rect(parent: Rect) -> Rect {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn client_size(window: &Window) -> Option<ClientSize> {
|
||||||
|
let size = window.geometry().size;
|
||||||
|
|
||||||
|
(size.w > 0 && size.h > 0).then_some(ClientSize {
|
||||||
|
width: size.w,
|
||||||
|
height: size.h,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ClientState {
|
pub struct ClientState {
|
||||||
pub compositor_state: CompositorClientState,
|
pub compositor_state: CompositorClientState,
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
use std::{error::Error, time::Duration};
|
use std::{
|
||||||
|
error::Error,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
@@ -74,6 +77,7 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
|
|||||||
}
|
}
|
||||||
WinitEvent::Input(event) => state.process_input_event(event),
|
WinitEvent::Input(event) => state.process_input_event(event),
|
||||||
WinitEvent::Redraw => {
|
WinitEvent::Redraw => {
|
||||||
|
state.advance_animations(Instant::now());
|
||||||
let damage = Rectangle::from_size(backend.window_size());
|
let damage = Rectangle::from_size(backend.window_size());
|
||||||
let mut presentation = OutputPresentationFeedback::new(&output);
|
let mut presentation = OutputPresentationFeedback::new(&output);
|
||||||
state.space.elements().for_each(|window| {
|
state.space.elements().for_each(|window| {
|
||||||
|
|||||||
Reference in new issue
Block a user