tiling: Animate tree changes

This commit is contained in:
Victoria Brekenfeld 2023-05-12 20:01:37 +02:00
parent ea1b976076
commit 331b884f1e
23 changed files with 1641 additions and 395 deletions

959
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -22,7 +22,6 @@ log-panics = { version = "2", features = ["with-backtrace"] }
thiserror = "1.0.26"
regex = "1"
xcursor = "0.3.3"
id_tree = "1.8.0"
xkbcommon = "0.4"
indexmap = "1.8.0"
xdg = "^2.1"
@ -40,6 +39,11 @@ tracing-journald = "0.3.0"
tracing = { version = "0.1.37", features = ["max_level_debug", "release_max_level_info"] }
puffin = { version = "0.14.3", optional = true }
puffin_egui = { version = "0.21.0", optional = true }
cosmic-time = "0.2.0"
[dependencies.id_tree]
git = "https://github.com/Drakulix/id-tree.git"
branch = "feature/copy_clone"
[dependencies.smithay]
version = "0.3"
@ -70,4 +74,4 @@ debug = true
lto = "fat"
[patch."https://github.com/Smithay/smithay.git"]
smithay = { git = "https://github.com/smithay//smithay", rev = "25d1176484" }
smithay = { git = "https://github.com/pop-os/smithay", branch = "tiling_rework" }

View file

@ -485,7 +485,9 @@ impl State {
}
surface.pending = false;
surface.dirty.then(|| {
(surface.dirty
|| data.state.common.shell.animations_going())
.then(|| {
(surface.output.clone(), surface.fps.avg_rendertime(5))
})
}

View file

@ -162,6 +162,7 @@ where
surface,
position.to_physical_precise_round(scale),
scale,
1.0,
)
}
@ -190,6 +191,7 @@ where
surface,
location.into().to_physical_precise_round(scale),
scale,
1.0,
)
}

View file

@ -118,6 +118,16 @@ where
CosmicElement::Egui(elem) => elem.opaque_regions(scale),
}
}
fn alpha(&self) -> f32 {
match self {
CosmicElement::Workspace(elem) => elem.alpha(),
CosmicElement::Cursor(elem) => elem.alpha(),
CosmicElement::MoveGrab(elem) => elem.alpha(),
#[cfg(feature = "debug")]
CosmicElement::Egui(elem) => elem.alpha(),
}
}
}
impl RenderElement<GlowRenderer> for CosmicElement<GlowRenderer> {

View file

@ -66,7 +66,7 @@ pub type GlMultiFrame<'a, 'b, 'frame> =
MultiFrame<'a, 'a, 'b, 'frame, GbmGlesBackend<GlowRenderer>, GbmGlesBackend<GlowRenderer>>;
pub static CLEAR_COLOR: [f32; 4] = [0.153, 0.161, 0.165, 1.0];
pub static FOCUS_INDICATOR_COLOR: [f32; 4] = [0.580, 0.921, 0.921, 1.0];
pub static FOCUS_INDICATOR_COLOR: [f32; 3] = [0.580, 0.921, 0.921];
pub static FOCUS_INDICATOR_SHADER: &str = include_str!("./shaders/focus_indicator.frag");
pub struct IndicatorShader(pub GlesPixelProgram);
@ -87,6 +87,7 @@ impl IndicatorShader {
renderer: &R,
geo: Rectangle<i32, Logical>,
thickness: u8,
alpha: f32,
) -> PixelShaderElement {
let thickness: f32 = thickness as f32;
let thickness_loc = (thickness as i32, thickness as i32);
@ -110,15 +111,14 @@ impl IndicatorShader {
}
None => {
let shader = Self::get(renderer);
let color = FOCUS_INDICATOR_COLOR;
let elem = PixelShaderElement::new(
shader,
dbg!(geo),
geo,
None, //TODO
color[3],
alpha,
vec![
Uniform::new("color", [color[0], color[1], color[2]]),
Uniform::new("color", FOCUS_INDICATOR_COLOR),
Uniform::new("thickness", thickness),
Uniform::new("radius", thickness * 2.0),
],
@ -283,6 +283,11 @@ where
}
}
state
.shell
.space_for_handle_mut(&handle)
.ok_or(OutputNoMode)?
.update_animations(&state.event_loop_handle);
let workspace = state.shell.space_for_handle(&handle).ok_or(OutputNoMode)?;
let last_active_seat = state.last_active_seat().clone();
let move_active = last_active_seat

View file

@ -95,7 +95,7 @@ impl WinitState {
.unwrap_or_default(),
0,
wp_presentation_feedback::Kind::Vsync,
)
);
}
}
Err(err) => {

View file

@ -66,6 +66,10 @@ fn main() -> Result<()> {
}
// trigger routines
data.state
.common
.shell
.update_animations(&data.state.common.event_loop_handle);
data.state.common.shell.refresh();
state::Common::refresh_focus(&mut data.state);

View file

@ -11,7 +11,10 @@ use smithay::{
backend::{
input::KeyState,
renderer::{
element::{AsRenderElements, Element, RenderElement, UnderlyingStorage},
element::{
utils::CropRenderElement, AsRenderElements, Element, RenderElement,
UnderlyingStorage,
},
gles::element::PixelShaderElement,
glow::GlowRenderer,
multigpu::Error as MultiError,
@ -418,15 +421,17 @@ impl CosmicMapped {
}
}
pub fn configure(&self) {
for window in match &self.element {
pub fn configure(&self) -> Option<Serial> {
match &self.element {
CosmicMappedInternal::Stack(s) => {
Box::new(s.surfaces()) as Box<dyn Iterator<Item = CosmicSurface>>
let active = s.active();
for surface in s.surfaces().filter(|s| s != &active) {
surface.send_configure();
}
CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.surface())),
active.send_configure()
}
CosmicMappedInternal::Window(w) => w.surface().send_configure(),
_ => unreachable!(),
} {
window.send_configure();
}
}
@ -676,6 +681,8 @@ where
{
Stack(self::stack::CosmicStackRenderElement<R>),
Window(self::window::CosmicWindowRenderElement<R>),
CroppedStack(CropRenderElement<self::stack::CosmicStackRenderElement<R>>),
CroppedWindow(CropRenderElement<self::window::CosmicWindowRenderElement<R>>),
Indicator(PixelShaderElement),
#[cfg(feature = "debug")]
Egui(TextureRenderElement<GlesTexture>),
@ -690,6 +697,8 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.id(),
CosmicMappedRenderElement::Window(elem) => elem.id(),
CosmicMappedRenderElement::CroppedStack(elem) => elem.id(),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.id(),
CosmicMappedRenderElement::Indicator(elem) => elem.id(),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.id(),
@ -700,6 +709,8 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.current_commit(),
CosmicMappedRenderElement::Window(elem) => elem.current_commit(),
CosmicMappedRenderElement::CroppedStack(elem) => elem.current_commit(),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.current_commit(),
CosmicMappedRenderElement::Indicator(elem) => elem.current_commit(),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.current_commit(),
@ -710,6 +721,8 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.src(),
CosmicMappedRenderElement::Window(elem) => elem.src(),
CosmicMappedRenderElement::CroppedStack(elem) => elem.src(),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.src(),
CosmicMappedRenderElement::Indicator(elem) => elem.src(),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.src(),
@ -720,6 +733,8 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.geometry(scale),
CosmicMappedRenderElement::Window(elem) => elem.geometry(scale),
CosmicMappedRenderElement::CroppedStack(elem) => elem.geometry(scale),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.geometry(scale),
CosmicMappedRenderElement::Indicator(elem) => elem.geometry(scale),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.geometry(scale),
@ -730,6 +745,8 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.location(scale),
CosmicMappedRenderElement::Window(elem) => elem.location(scale),
CosmicMappedRenderElement::CroppedStack(elem) => elem.location(scale),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.location(scale),
CosmicMappedRenderElement::Indicator(elem) => elem.location(scale),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.location(scale),
@ -740,6 +757,8 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.transform(),
CosmicMappedRenderElement::Window(elem) => elem.transform(),
CosmicMappedRenderElement::CroppedStack(elem) => elem.transform(),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.transform(),
CosmicMappedRenderElement::Indicator(elem) => elem.transform(),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.transform(),
@ -754,6 +773,8 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::Window(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::CroppedStack(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::Indicator(elem) => elem.damage_since(scale, commit),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.damage_since(scale, commit),
@ -764,11 +785,25 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::Window(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::CroppedStack(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::Indicator(elem) => elem.opaque_regions(scale),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.opaque_regions(scale),
}
}
fn alpha(&self) -> f32 {
match self {
CosmicMappedRenderElement::Stack(elem) => elem.alpha(),
CosmicMappedRenderElement::Window(elem) => elem.alpha(),
CosmicMappedRenderElement::CroppedStack(elem) => elem.alpha(),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.alpha(),
CosmicMappedRenderElement::Indicator(elem) => elem.alpha(),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.alpha(),
}
}
}
impl RenderElement<GlowRenderer> for CosmicMappedRenderElement<GlowRenderer> {
@ -782,6 +817,8 @@ impl RenderElement<GlowRenderer> for CosmicMappedRenderElement<GlowRenderer> {
match self {
CosmicMappedRenderElement::Stack(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::Window(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::CroppedStack(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::Indicator(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage)
}
@ -796,6 +833,8 @@ impl RenderElement<GlowRenderer> for CosmicMappedRenderElement<GlowRenderer> {
match self {
CosmicMappedRenderElement::Stack(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::Window(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::CroppedStack(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::Indicator(elem) => elem.underlying_storage(renderer),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.underlying_storage(renderer),
@ -816,6 +855,8 @@ impl<'a, 'b> RenderElement<GlMultiRenderer<'a, 'b>>
match self {
CosmicMappedRenderElement::Stack(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::Window(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::CroppedStack(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::Indicator(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame.glow_frame_mut(), src, dst, damage)
.map_err(|err| MultiError::Render(err))
@ -836,6 +877,8 @@ impl<'a, 'b> RenderElement<GlMultiRenderer<'a, 'b>>
match self {
CosmicMappedRenderElement::Stack(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::Window(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::CroppedStack(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::Indicator(elem) => {
elem.underlying_storage(renderer.glow_renderer_mut())
}
@ -910,6 +953,7 @@ where
renderer: &mut R,
location: Point<i32, Physical>,
scale: Scale<f64>,
alpha: f32,
) -> Vec<C> {
#[cfg(feature = "debug")]
let mut elements = if let Some(debug) = self.debug.lock().unwrap().as_mut() {
@ -1080,12 +1124,12 @@ where
CosmicMappedInternal::Stack(s) => {
elements.extend(AsRenderElements::<R>::render_elements::<
CosmicMappedRenderElement<R>,
>(s, renderer, location, scale))
>(s, renderer, location, scale, alpha))
}
CosmicMappedInternal::Window(w) => {
elements.extend(AsRenderElements::<R>::render_elements::<
CosmicMappedRenderElement<R>,
>(w, renderer, location, scale))
>(w, renderer, location, scale, alpha))
}
_ => {}
};

View file

@ -604,9 +604,10 @@ where
renderer: &mut R,
mut location: Point<i32, Physical>,
scale: Scale<f64>,
alpha: f32,
) -> Vec<C> {
let mut elements = AsRenderElements::<R>::render_elements::<CosmicStackRenderElement<R>>(
&self.0, renderer, location, scale,
&self.0, renderer, location, scale, alpha,
);
location.y += TAB_HEIGHT;
@ -616,6 +617,7 @@ where
renderer,
location,
scale,
alpha,
);
elements
}));

View file

@ -29,7 +29,7 @@ use smithay::{
wayland_server::protocol::wl_surface::WlSurface,
},
space_elements,
utils::{user_data::UserDataMap, Logical, Rectangle, Size},
utils::{user_data::UserDataMap, Logical, Rectangle, Serial, Size},
wayland::{
compositor::{with_states, SurfaceData},
seat::WaylandFocus,
@ -345,11 +345,44 @@ impl CosmicSurface {
})
}
pub fn send_configure(&self) {
pub fn serial_acked(&self, serial: &Serial) -> bool {
match self {
CosmicSurface::Wayland(window) => window.toplevel().send_configure(),
CosmicSurface::Wayland(window) => {
with_states(window.toplevel().wl_surface(), |states| {
let attrs = states
.data_map
.get::<XdgToplevelSurfaceData>()
.unwrap()
.lock()
.unwrap();
attrs
.configure_serial
.as_ref()
.map(|s| s >= serial)
.unwrap_or(false)
})
}
_ => true,
}
}
pub fn force_configure(&self) -> Option<Serial> {
match self {
CosmicSurface::Wayland(window) => Some(window.toplevel().send_configure()),
CosmicSurface::X11(surface) => {
let _ = surface.configure(None);
None
}
_ => unreachable!(),
}
}
pub fn send_configure(&self) -> Option<Serial> {
match self {
CosmicSurface::Wayland(window) => window.toplevel().send_pending_configure(),
CosmicSurface::X11(surface) => {
let _ = surface.configure(None);
None
}
_ => unreachable!(),
}
@ -675,10 +708,15 @@ where
renderer: &mut R,
location: smithay::utils::Point<i32, smithay::utils::Physical>,
scale: smithay::utils::Scale<f64>,
alpha: f32,
) -> Vec<C> {
match self {
CosmicSurface::Wayland(window) => window.render_elements(renderer, location, scale),
CosmicSurface::X11(surface) => surface.render_elements(renderer, location, scale),
CosmicSurface::Wayland(window) => {
window.render_elements(renderer, location, scale, alpha)
}
CosmicSurface::X11(surface) => {
surface.render_elements(renderer, location, scale, alpha)
}
_ => unreachable!(),
}
}

View file

@ -640,6 +640,13 @@ where
CosmicWindowRenderElement::Window(w) => w.opaque_regions(scale),
}
}
fn alpha(&self) -> f32 {
match self {
CosmicWindowRenderElement::Header(h) => h.alpha(),
CosmicWindowRenderElement::Window(w) => w.alpha(),
}
}
}
impl RenderElement<GlowRenderer> for CosmicWindowRenderElement<GlowRenderer> {
@ -710,6 +717,7 @@ where
renderer: &mut R,
location: Point<i32, Physical>,
scale: Scale<f64>,
alpha: f32,
) -> Vec<C> {
let has_ssd = self.0.with_program(|p| p.has_ssd());
@ -721,14 +729,18 @@ where
let mut elements = self.0.with_program(|p| {
AsRenderElements::<R>::render_elements::<CosmicWindowRenderElement<R>>(
&p.window, renderer, window_loc, scale,
&p.window, renderer, window_loc, scale, alpha,
)
});
if has_ssd {
elements.extend(AsRenderElements::<GlowRenderer>::render_elements::<
CosmicWindowRenderElement<R>,
>(
&self.0, renderer.glow_renderer_mut(), location, scale
&self.0,
renderer.glow_renderer_mut(),
location,
scale,
alpha,
))
}

View file

@ -68,6 +68,7 @@ impl MoveGrabState {
renderer,
Rectangle::from_loc_and_size(render_location, self.window.geometry().size),
self.indicator_thickness,
1.0,
))
.into(),
);
@ -77,6 +78,7 @@ impl MoveGrabState {
renderer,
(render_location - self.window.geometry().loc).to_physical_precise_round(scale),
scale,
1.0,
));
elements
}
@ -282,7 +284,10 @@ impl MoveSurfaceGrab {
if let Some(position) = position {
handle.motion(
state,
Some((PointerFocusTarget::from(self.window.clone()), position - self.window.geometry().loc)),
Some((
PointerFocusTarget::from(self.window.clone()),
position - self.window.geometry().loc,
)),
&MotionEvent {
location: handle.current_location(),
serial: serial,

View file

@ -374,6 +374,7 @@ impl FloatingLayout {
renderer,
render_location.to_physical_precise_round(output_scale),
output_scale.into(),
1.0,
);
if focused == Some(elem) {
if indicator_thickness > 0 {
@ -384,6 +385,7 @@ impl FloatingLayout {
elem.geometry().size,
),
indicator_thickness,
1.0,
);
elements.insert(0, element.into());
}

View file

@ -0,0 +1,65 @@
use crate::{
shell::element::CosmicSurface, state::Data,
wayland::handlers::compositor::client_compositor_state,
};
use calloop::LoopHandle;
use smithay::{
reexports::wayland_server::{backend::ClientId, Client, Resource},
utils::Serial,
wayland::{
compositor::{Blocker, BlockerState},
seat::WaylandFocus,
},
};
use std::{
collections::HashMap,
time::{Duration, Instant},
};
#[derive(Debug, Clone)]
pub struct TilingBlocker {
pub necessary_acks: Vec<(CosmicSurface, Serial)>,
start: Instant,
}
impl Blocker for TilingBlocker {
fn state(&self) -> BlockerState {
if self.is_ready() {
BlockerState::Released
} else {
BlockerState::Pending
}
}
}
impl TilingBlocker {
pub fn new(configures: impl IntoIterator<Item = (CosmicSurface, Serial)>) -> Self {
TilingBlocker {
necessary_acks: configures.into_iter().collect(),
start: Instant::now(),
}
}
pub fn is_ready(&self) -> bool {
Instant::now().duration_since(self.start) >= Duration::from_millis(200)
|| self
.necessary_acks
.iter()
.all(|(surf, serial)| surf.serial_acked(serial))
}
pub fn signal_ready(&self, handle: &LoopHandle<'static, Data>) {
let clients = self
.necessary_acks
.iter()
.flat_map(|(surface, _)| surface.wl_surface().and_then(|s| s.client()))
.map(|client| (client.id(), client))
.collect::<HashMap<ClientId, Client>>();
handle.insert_idle(move |data| {
let dh = data.display.handle();
for client in clients.values() {
client_compositor_state(&client).blocker_cleared(&mut data.state, &dh);
}
});
}
}

View file

@ -64,7 +64,8 @@ impl PointerGrab<State> for ResizeForkGrab {
if let Some(output) = self.output.upgrade() {
let tiling_layer = &mut data.common.shell.active_space_mut(&output).tiling_layer;
if let Some(tree) = tiling_layer.trees.get_mut(&output) {
if let Some(queue) = tiling_layer.queues.get_mut(&output) {
let tree = &mut queue.trees.back_mut().unwrap().0;
if tree.get(&self.node).is_ok() {
let orientation = tree.get(&self.node).unwrap().data().orientation();
let delta = match orientation {

View file

@ -18,24 +18,36 @@ use crate::{
},
};
use calloop::LoopHandle;
use cosmic_time::{Cubic, Ease, Tween};
use id_tree::{InsertBehavior, MoveBehavior, Node, NodeId, NodeIdError, RemoveBehavior, Tree};
use smithay::{
backend::renderer::{
element::{AsRenderElements, RenderElement},
element::{utils::CropRenderElement, AsRenderElements, RenderElement},
ImportAll, ImportMem, Renderer,
},
desktop::{layer_map_for_output, space::SpaceElement, PopupKind},
input::{pointer::GrabStartData as PointerGrabStartData, Seat},
output::Output,
utils::{IsAlive, Logical, Point, Rectangle, Scale},
wayland::seat::WaylandFocus,
wayland::{compositor::add_blocker, seat::WaylandFocus},
};
use std::{
borrow::Borrow,
collections::{HashMap, VecDeque},
hash::Hash,
sync::Arc,
time::{Duration, Instant},
};
use std::{borrow::Borrow, collections::HashMap, hash::Hash, sync::Arc};
use tracing::trace;
mod blocker;
mod grabs;
pub use self::blocker::*;
pub use self::grabs::*;
const ANIMATION_DURATION: Duration = Duration::from_millis(200);
#[derive(Debug, Clone)]
struct OutputData {
output: Output,
@ -83,10 +95,24 @@ pub enum FocusResult {
Some(KeyboardFocusTarget),
}
#[derive(Debug, Clone, Default)]
struct TreeQueue {
trees: VecDeque<(Tree<Data>, Option<TilingBlocker>)>,
animation_start: Option<Instant>,
}
impl TreeQueue {
pub fn push_tree(&mut self, tree: Tree<Data>, blocker: Option<TilingBlocker>) {
self.trees.push_back((tree, blocker))
}
}
#[derive(Debug, Clone)]
pub struct TilingLayout {
gaps: (i32, i32),
trees: HashMap<OutputData, Tree<Data>>,
queues: HashMap<OutputData, TreeQueue>,
standby_tree: Option<Tree<Data>>,
pending_blockers: Vec<TilingBlocker>,
}
#[derive(Debug, Clone)]
@ -251,24 +277,33 @@ impl TilingLayout {
pub fn new(gaps: (u8, u8)) -> TilingLayout {
TilingLayout {
gaps: (gaps.0 as i32, gaps.1 as i32),
trees: HashMap::new(),
queues: HashMap::new(),
standby_tree: None,
pending_blockers: Vec::new(),
}
}
}
impl TilingLayout {
pub fn map_output(&mut self, output: &Output, location: Point<i32, Logical>) {
if !self.trees.contains_key(output) {
self.trees.insert(
if !self.queues.contains_key(output) {
self.queues.insert(
OutputData {
output: output.clone(),
location,
},
Tree::new(),
TreeQueue {
trees: {
let mut queue = VecDeque::new();
queue.push_back((self.standby_tree.take().unwrap_or_else(Tree::new), None));
queue
},
animation_start: None,
},
);
} else {
let tree = self.trees.remove(output).unwrap();
self.trees.insert(
let tree = self.queues.remove(output).unwrap();
self.queues.insert(
OutputData {
output: output.clone(),
location,
@ -283,9 +318,19 @@ impl TilingLayout {
output: &Output,
toplevel_info: &mut ToplevelInfoState<State, CosmicSurface>,
) {
if let Some(src) = self.trees.remove(output) {
// TODO: expects last remaining output
let Some((new_output, dst)) = self.trees.iter_mut().next() else { return; };
if let Some(mut src) = self.queues.remove(output) {
// Operate on last pending tree & unblock queue
for blocker in src.trees.iter_mut().flat_map(|(_, blocker)| blocker.take()) {
self.pending_blockers.push(blocker);
}
let (src, _) = src.trees.pop_back().expect("No tree in queue");
let Some((new_output, dst_queue)) = self.queues.iter_mut().next() else {
self.standby_tree = Some(src);
return;
};
let mut dst = dst_queue.trees.back().unwrap().0.copy_clone();
let orientation = match new_output.output.geometry().size {
x if x.w >= x.h => Orientation::Vertical,
_ => Orientation::Horizontal,
@ -309,8 +354,10 @@ impl TilingLayout {
mapped.output_enter(&new_output.output, mapped.bbox());
}
}
TilingLayout::merge_trees(src, dst, orientation);
self.refresh()
TilingLayout::merge_trees(src, &mut dst, orientation);
let blocker = TilingLayout::update_positions(output, &mut dst, self.gaps);
dst_queue.push_tree(dst, blocker);
}
}
@ -324,7 +371,6 @@ impl TilingLayout {
window.output_enter(&output, window.bbox());
window.set_bounds(output.geometry().size);
self.map_internal(window, &output, Some(focus_stack));
self.refresh();
}
fn map_internal<'a>(
@ -333,15 +379,17 @@ impl TilingLayout {
output: &Output,
focus_stack: Option<impl Iterator<Item = &'a CosmicMapped> + 'a>,
) {
let tree = self.trees.get_mut(output).expect("Output not mapped?");
let queue = self.queues.get_mut(output).expect("Output not mapped?");
let mut tree = queue.trees.back().unwrap().0.copy_clone();
let window = window.into();
let new_window = Node::new(Data::Mapped {
mapped: window.clone(),
last_geometry: Rectangle::from_loc_and_size((0, 0), (100, 100)),
});
let last_active =
focus_stack.and_then(|focus_stack| TilingLayout::last_active_window(tree, focus_stack));
let last_active = focus_stack
.and_then(|focus_stack| TilingLayout::last_active_window(&mut tree, focus_stack));
let window_id = if let Some((_last_active_window, ref node_id)) = last_active {
let orientation = {
@ -353,7 +401,7 @@ impl TilingLayout {
}
};
let new_id = tree.insert(new_window, InsertBehavior::AsRoot).unwrap();
TilingLayout::new_group(tree, &node_id, &new_id, orientation).unwrap();
TilingLayout::new_group(&mut tree, &node_id, &new_id, orientation).unwrap();
new_id
} else {
// nothing? then we add to the root
@ -367,7 +415,7 @@ impl TilingLayout {
}
};
let new_id = tree.insert(new_window, InsertBehavior::AsRoot).unwrap();
TilingLayout::new_group(tree, &root_id, &new_id, orientation).unwrap();
TilingLayout::new_group(&mut tree, &root_id, &new_id, orientation).unwrap();
new_id
} else {
tree.insert(new_window, InsertBehavior::AsRoot).unwrap()
@ -375,15 +423,23 @@ impl TilingLayout {
};
*window.tiling_node_id.lock().unwrap() = Some(window_id);
let blocker = TilingLayout::update_positions(output, &mut tree, self.gaps);
queue.push_tree(tree, blocker);
}
pub fn unmap(&mut self, window: &CosmicMapped) -> Option<Output> {
let output = {
let node_id = window.tiling_node_id.lock().unwrap().clone()?;
self.trees
self.queues
.iter()
.find(|(_, tree)| {
tree.get(&node_id)
.find(|(_, queue)| {
queue
.trees
.back()
.unwrap()
.0
.get(&node_id)
.map(|node| node.data().is_mapped(Some(window)))
.unwrap_or(false)
})
@ -391,19 +447,23 @@ impl TilingLayout {
};
self.unmap_window_internal(window);
window.output_leave(&output);
window.set_tiled(false);
self.refresh();
Some(output)
}
fn unmap_window_internal(&mut self, mapped: &CosmicMapped) {
if let Some(node_id) = mapped.tiling_node_id.lock().unwrap().as_ref() {
if let Some(tree) = self.trees.values_mut().find(|tree| {
tree.get(node_id)
let tiling_node_id = mapped.tiling_node_id.lock().unwrap().as_ref().cloned();
if let Some(node_id) = tiling_node_id {
if let Some((output, queue)) = self.queues.iter_mut().find(|(_, queue)| {
let tree = &queue.trees.back().unwrap().0;
tree.get(&node_id)
.map(|node| node.data().is_mapped(Some(mapped)))
.unwrap_or(false)
}) {
let mut tree = queue.trees.back().unwrap().0.copy_clone();
let parent_id = tree
.get(&node_id)
.ok()
@ -412,7 +472,7 @@ impl TilingLayout {
let position = parent_id.as_ref().and_then(|parent_id| {
tree.children_ids(&parent_id)
.unwrap()
.position(|id| id == node_id)
.position(|id| id == &node_id)
});
let parent_parent_id = parent_id.as_ref().and_then(|parent_id| {
tree.get(parent_id)
@ -423,7 +483,7 @@ impl TilingLayout {
// remove self
trace!(?mapped, "Remove window.");
let _ = tree.remove_node(node_id.clone(), RemoveBehavior::DropChildren);
let _ = tree.remove_node(node_id, RemoveBehavior::DropChildren);
// fixup parent node
match parent_id {
@ -457,6 +517,9 @@ impl TilingLayout {
}
None => {} // root
}
let blocker = TilingLayout::update_positions(&output.output, &mut tree, self.gaps);
queue.push_tree(tree, blocker);
}
}
}
@ -465,11 +528,12 @@ impl TilingLayout {
self.mapped().find_map(|(o, m, _)| (m == elem).then_some(o))
}
// TODO: Move would needs this to be accurate during animations
pub fn element_geometry(&self, elem: &CosmicMapped) -> Option<Rectangle<i32, Logical>> {
if let Some(id) = elem.tiling_node_id.lock().unwrap().as_ref() {
if let Some(output) = self.output_for_element(elem) {
let (output_data, tree) = self.trees.get_key_value(output).unwrap();
let node = tree.get(id).ok()?;
let (output_data, queue) = self.queues.get_key_value(output).unwrap();
let node = queue.trees.back().unwrap().0.get(id).ok()?;
let data = node.data();
assert!(data.is_mapped(Some(elem)));
let mut geo = *data.geometry();
@ -486,9 +550,10 @@ impl TilingLayout {
seat: &Seat<State>,
) -> Option<CosmicMapped /*TODO move window groups across screens?*/> {
let output = seat.active_output();
let tree = self.trees.get_mut(&output).unwrap();
let queue = self.queues.get_mut(&output).unwrap();
let mut tree = queue.trees.back().unwrap().0.copy_clone();
let node_id = match TilingLayout::currently_focused_node(tree, seat) {
let node_id = match TilingLayout::currently_focused_node(&mut tree, seat) {
Some(node_id) => node_id,
None => {
return None;
@ -533,7 +598,7 @@ impl TilingLayout {
| (Orientation::Vertical, Direction::Down)
) {
TilingLayout::new_group(
tree,
&mut tree,
&parent,
&node_id,
match direction {
@ -556,7 +621,9 @@ impl TilingLayout {
.unwrap()
.data_mut()
.remove_window(og_idx);
self.refresh();
let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps);
queue.push_tree(tree, blocker);
return None;
}
@ -580,7 +647,9 @@ impl TilingLayout {
.unwrap()
.data_mut()
.remove_window(og_idx);
self.refresh();
let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps);
queue.push_tree(tree, blocker);
return None;
}
@ -646,7 +715,7 @@ impl TilingLayout {
.unwrap()
.clone();
TilingLayout::new_group(
tree,
&mut tree,
&old_id,
&node_id,
!group_orientation,
@ -678,7 +747,8 @@ impl TilingLayout {
.swap_windows(idx, next_idx);
} else {
// else we make a new fork
TilingLayout::new_group(tree, &next_child_id, &node_id, orientation).unwrap();
TilingLayout::new_group(&mut tree, &next_child_id, &node_id, orientation)
.unwrap();
tree.make_nth_sibling(
&node_id,
if direction == Direction::Left || direction == Direction::Up {
@ -693,7 +763,9 @@ impl TilingLayout {
.data_mut()
.remove_window(og_idx);
}
self.refresh();
let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps);
queue.push_tree(tree, blocker);
return None;
}
@ -715,7 +787,7 @@ impl TilingLayout {
focus_stack: impl Iterator<Item = &'a CosmicMapped> + 'a,
) -> FocusResult {
let output = seat.active_output();
let tree = self.trees.get_mut(&output).unwrap();
let tree = &self.queues.get(&output).unwrap().trees.back().unwrap().0;
// TODO: Rather use something like seat.current_keyboard_focus
// TODO https://github.com/Smithay/smithay/pull/777
@ -860,8 +932,10 @@ impl TilingLayout {
focus_stack: impl Iterator<Item = &'a CosmicMapped> + 'a,
) {
let output = seat.active_output();
let tree = self.trees.get_mut(&output).unwrap();
if let Some((_, last_active)) = TilingLayout::last_active_window(tree, focus_stack) {
let Some(queue) = self.queues.get_mut(&output) else { return };
let mut tree = queue.trees.back().unwrap().0.copy_clone();
if let Some((_, last_active)) = TilingLayout::last_active_window(&tree, focus_stack) {
if let Some(group) = tree.get(&last_active).unwrap().parent().cloned() {
if let &mut Data::Group {
ref mut orientation,
@ -890,13 +964,15 @@ impl TilingLayout {
}
*orientation = new_orientation;
let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps);
queue.push_tree(tree, blocker);
}
}
}
self.refresh();
}
pub fn refresh<'a>(&mut self) {
pub fn refresh(&mut self) {
#[cfg(feature = "debug")]
puffin::profile_function!();
@ -908,53 +984,47 @@ impl TilingLayout {
for dead_window in dead_windows.iter() {
self.unmap_window_internal(&dead_window);
}
// flatten trees
for tree in self.trees.values_mut() {
let root_id = match tree.root_node_id() {
Some(root) => root,
None => {
continue;
}
};
for node_id in tree
.traverse_pre_order_ids(root_id)
.unwrap()
.collect::<Vec<_>>()
.into_iter()
{
let node = tree.get(&node_id).unwrap();
let data = node.data();
if data.is_group() && data.len() == 1 {
// RemoveBehavior::LiftChildren sadly does not what we want: lifting them into the same place.
// So we need to fix that manually..
let child_id = tree
.children_ids(&node_id)
.unwrap()
.cloned()
.next()
.unwrap();
let idx = node.parent().map(|parent_id| {
tree.children_ids(&parent_id)
.unwrap()
.position(|id| id == &node_id)
.unwrap()
});
tree.remove_node(node_id, RemoveBehavior::LiftChildren)
.unwrap();
if let Some(idx) = idx {
tree.make_nth_sibling(&child_id, idx).unwrap();
} else {
// additionally `RemoveBehavior::LiftChildren` doesn't work, when removing the root-node,
// even with just one child. *sigh*
tree.move_node(&child_id, MoveBehavior::ToRoot).unwrap();
}
}
}
}
for (_, mapped, _) in self.mapped() {
mapped.refresh();
}
TilingLayout::update_space_positions(&mut self.trees, self.gaps);
}
pub fn animations_going(&self) -> bool {
self.queues
.values()
.any(|queue| queue.animation_start.is_some())
}
pub fn update_animation_state(&mut self, handle: &LoopHandle<'static, crate::state::Data>) {
for blocker in self.pending_blockers.drain(..) {
blocker.signal_ready(handle);
}
for queue in self.queues.values_mut() {
if let Some(start) = queue.animation_start {
let duration_since_start = Instant::now().duration_since(start);
if duration_since_start > ANIMATION_DURATION {
assert!(queue.trees.len() >= 2);
let _ = queue.animation_start.take();
let _ = queue.trees.pop_front();
let _ = queue.trees.front_mut().unwrap().1.take();
} else {
continue;
}
}
if let Some((_, blocker)) = queue.trees.get(1) {
if blocker
.as_ref()
.map(TilingBlocker::is_ready)
.unwrap_or(true)
{
queue.animation_start = Some(Instant::now());
if let Some(blocker) = blocker.as_ref() {
blocker.signal_ready(handle)
}
}
}
}
}
pub fn resize_request(
@ -964,7 +1034,8 @@ impl TilingLayout {
start_data: PointerGrabStartData<State>,
edges: ResizeEdge,
) -> Option<ResizeForkGrab> {
let (output, mut node_id) = self.trees.iter().find_map(|(output, tree)| {
let (output, mut node_id) = self.queues.iter().find_map(|(output, queue)| {
let tree = &queue.trees.back().unwrap().0;
let root_id = tree.root_node_id()?;
tree.traverse_pre_order_ids(root_id)
.unwrap()
@ -972,7 +1043,8 @@ impl TilingLayout {
.map(|id| (&output.output, id))
})?;
let tree = self.trees.get(output).unwrap();
let queue = self.queues.get(output).unwrap();
let tree = &queue.trees.back().unwrap().0;
while let Some(group_id) = tree.get(&node_id).unwrap().parent() {
let orientation = tree.get(group_id).unwrap().data().orientation();
if !((orientation == Orientation::Vertical
@ -1010,7 +1082,7 @@ impl TilingLayout {
}
fn last_active_window<'a>(
tree: &mut Tree<Data>,
tree: &Tree<Data>,
mut focus_stack: impl Iterator<Item = &'a CosmicMapped>,
) -> Option<(CosmicMapped, NodeId)> {
focus_stack
@ -1110,16 +1182,18 @@ impl TilingLayout {
Ok(group_id)
}
fn update_space_positions(trees: &mut HashMap<OutputData, Tree<Data>>, gaps: (i32, i32)) {
fn update_positions(
output: &Output,
tree: &mut Tree<Data>,
gaps: (i32, i32),
) -> Option<TilingBlocker> {
#[cfg(feature = "debug")]
puffin::profile_function!();
if let Some(root_id) = tree.root_node_id() {
let mut configures = Vec::new();
let (outer, inner) = gaps;
for (output, tree) in trees
.iter_mut()
.map(|(output_data, tree)| (&output_data.output, tree))
{
if let Some(root) = tree.root_node_id() {
let mut geo = layer_map_for_output(&output).non_exclusive_zone();
geo.loc.x += outer;
geo.loc.y += outer;
@ -1128,19 +1202,56 @@ impl TilingLayout {
let mut stack = vec![geo];
for node_id in tree
.traverse_pre_order_ids(root)
.traverse_pre_order_ids(root_id)
.unwrap()
.collect::<Vec<_>>()
.into_iter()
{
let node = tree.get_mut(&node_id).unwrap();
if let Some(mut geo) = stack.pop() {
let data = node.data_mut();
// flatten tree
if data.is_group() && data.len() == 1 {
// RemoveBehavior::LiftChildren sadly does not what we want: lifting them into the same place.
// So we need to fix that manually..
let idx = node.parent().cloned().map(|parent_id| {
tree.children_ids(&parent_id)
.unwrap()
.position(|id| id == &node_id)
.unwrap()
});
let child_id = tree
.children_ids(&node_id)
.unwrap()
.cloned()
.next()
.unwrap();
tree.remove_node(node_id, RemoveBehavior::LiftChildren)
.unwrap();
if let Some(idx) = idx {
tree.make_nth_sibling(&child_id, idx).unwrap();
} else {
// additionally `RemoveBehavior::LiftChildren` doesn't work, when removing the root-node,
// even with just one child. *sigh*
tree.move_node(&child_id, MoveBehavior::ToRoot).unwrap();
}
continue;
}
if let Some(mut geo) = stack.pop() {
let node = tree.get_mut(&node_id).unwrap();
let data = node.data_mut();
if data.is_mapped(None) {
geo.loc += (inner, inner).into();
geo.size -= (inner * 2, inner * 2).into();
}
data.update_geometry(geo);
match data {
Data::Group {
orientation, sizes, ..
} => {
match orientation {
} => match orientation {
Orientation::Horizontal => {
let mut previous: i32 = sizes.iter().sum();
for size in sizes.iter().rev() {
@ -1161,24 +1272,18 @@ impl TilingLayout {
));
}
}
}
data.update_geometry(geo);
}
},
Data::Mapped { mapped, .. } => {
geo.loc += (inner, inner).into();
if !(mapped.is_fullscreen() || mapped.is_maximized()) {
mapped.set_tiled(true);
let size = (geo.size.w - inner * 2, geo.size.h - inner * 2);
let internal_geometry = Rectangle::from_loc_and_size(
geo.loc + output.geometry().loc,
size,
geo.size,
);
if mapped.geometry() != internal_geometry {
mapped.set_geometry(internal_geometry);
mapped.configure();
}
}
data.update_geometry(geo);
if let Some(serial) = mapped.configure() {
configures.push((mapped.active_window(), serial));
}
}
}
@ -1187,10 +1292,25 @@ impl TilingLayout {
}
}
if !configures.is_empty() {
let blocker = TilingBlocker::new(configures);
for (surface, _) in &blocker.necessary_acks {
if let Some(surface) = surface.wl_surface() {
add_blocker(&surface, blocker.clone());
}
}
return Some(blocker);
}
}
None
}
pub fn mapped(&self) -> impl Iterator<Item = (&Output, &CosmicMapped, Point<i32, Logical>)> {
self.trees
self.queues
.iter()
.flat_map(|(output_data, tree)| {
.flat_map(|(output_data, queue)| {
let tree = &queue.trees.back().unwrap().0;
if let Some(root) = tree.root_node_id() {
Some(
tree.traverse_pre_order(root)
@ -1252,15 +1372,20 @@ impl TilingLayout {
}
pub fn merge(&mut self, other: TilingLayout) {
for (output_data, src) in other.trees {
let mut dst = self.trees.entry(output_data.clone()).or_default();
for (output_data, mut src_queue) in other.queues {
let src = src_queue.trees.pop_back().unwrap().0;
let dst_queue = self.queues.entry(output_data.clone()).or_default();
let mut dst = dst_queue.trees.back().unwrap().0.copy_clone();
let orientation = match output_data.output.geometry().size {
x if x.w >= x.h => Orientation::Vertical,
_ => Orientation::Horizontal,
};
TilingLayout::merge_trees(src, &mut dst, orientation);
let blocker = TilingLayout::update_positions(&output_data.output, &mut dst, self.gaps);
dst_queue.push_tree(dst, blocker);
}
self.refresh();
}
fn merge_trees(src: Tree<Data>, dst: &mut Tree<Data>, orientation: Orientation) {
@ -1320,21 +1445,103 @@ impl TilingLayout {
let output_scale = output.current_scale().fractional_scale();
if !self.trees.contains_key(output) {
if !self.queues.contains_key(output) {
return Err(OutputNotMapped);
}
Ok(self
let queue = self.queues.get(output).unwrap();
let (target_tree, _) = if queue.animation_start.is_some() {
queue
.trees
.iter()
.flat_map(|(output_data, tree)| {
if &output_data.output != output {
return None;
.get(1)
.expect("Animation ongoing, should have two trees")
} else {
queue.trees.front().unwrap()
};
let reference_tree = queue
.animation_start
.is_some()
.then(|| &queue.trees.front().unwrap().0);
let percentage = if let Some(animation_start) = queue.animation_start {
let percentage = Instant::now().duration_since(animation_start).as_millis() as f32
/ ANIMATION_DURATION.as_millis() as f32;
Ease::Cubic(Cubic::Out).tween(percentage)
} else {
1.0
};
let mut elements = Vec::new();
// all old windows and fade them out
if let Some(reference_tree) = reference_tree.as_ref() {
if let Some(root) = reference_tree.root_node_id() {
elements.extend(
reference_tree
.traverse_pre_order(root)
.unwrap()
.filter(|node| node.data().is_mapped(None))
.map(|node| match node.data() {
Data::Mapped {
mapped,
last_geometry,
..
} => (mapped, last_geometry),
_ => unreachable!(),
})
.filter(|(mapped, _)| {
if let Some(root) = target_tree.root_node_id() {
!target_tree
.traverse_pre_order(root)
.unwrap()
.any(|node| node.data().is_mapped(Some(mapped)))
} else {
true
}
})
.flat_map(|(mapped, geo)| {
let crop_rect = geo.clone();
AsRenderElements::<R>::render_elements::<CosmicMappedRenderElement<R>>(
mapped,
renderer,
geo.loc.to_physical_precise_round(output_scale)
- mapped
.geometry()
.loc
.to_physical_precise_round(output_scale),
Scale::from(output_scale),
1.0 - percentage,
)
.into_iter()
.flat_map(|element| match element {
CosmicMappedRenderElement::Stack(elem) => {
CropRenderElement::from_element(
elem,
output_scale,
crop_rect.to_physical_precise_round(output_scale),
)
.map(CosmicMappedRenderElement::CroppedStack)
}
CosmicMappedRenderElement::Window(elem) => {
CropRenderElement::from_element(
elem,
output_scale,
crop_rect.to_physical_precise_round(output_scale),
)
.map(CosmicMappedRenderElement::CroppedWindow)
}
x => Some(x),
})
.collect::<Vec<_>>()
}),
)
}
}
if let Some(root) = tree.root_node_id() {
Some(
tree.traverse_pre_order(root)
if let Some(root) = target_tree.root_node_id() {
elements.extend(
target_tree
.traverse_pre_order(root)
.unwrap()
.filter(|node| node.data().is_mapped(None))
.filter(|node| match node.data() {
@ -1346,11 +1553,12 @@ impl TilingLayout {
mapped,
last_geometry,
..
} => (mapped, last_geometry.loc),
} => (mapped, last_geometry),
_ => unreachable!(),
})
.chain(
tree.traverse_pre_order(root)
target_tree
.traverse_pre_order(root)
.unwrap()
.filter(|node| node.data().is_mapped(None))
.filter(|node| match node.data() {
@ -1362,40 +1570,116 @@ impl TilingLayout {
mapped,
last_geometry,
..
} => (mapped, last_geometry.loc),
} => (mapped, last_geometry),
_ => unreachable!(),
}),
),
)
.flat_map(|(mapped, new_geo)| {
let old_geo = if let Some(reference_tree) = reference_tree.as_ref() {
if let Some(root) = reference_tree.root_node_id() {
reference_tree
.traverse_pre_order(root)
.unwrap()
.find(|node| node.data().is_mapped(Some(mapped)))
.map(|node| match node.data() {
Data::Mapped { last_geometry, .. } => last_geometry,
_ => unreachable!(),
})
} else {
None
}
})
.flatten()
.flat_map(|(mapped, loc)| {
} else {
None
};
let (geo, alpha) = if let Some(old_geo) = old_geo {
(
Rectangle::from_loc_and_size(
(
old_geo.loc.x
+ ((new_geo.loc.x - old_geo.loc.x) as f32 * percentage)
.round()
as i32,
old_geo.loc.y
+ ((new_geo.loc.y - old_geo.loc.y) as f32 * percentage)
.round()
as i32,
),
(
old_geo.size.w
+ ((new_geo.size.w - old_geo.size.w) as f32
* percentage)
.round()
as i32,
old_geo.size.h
+ ((new_geo.size.h - old_geo.size.h) as f32
* percentage)
.round()
as i32,
),
),
1.0,
)
} else {
// TODO: If old_geo.is_none() animate alpha - fade in
(*new_geo, percentage)
};
if alpha < 1.0 {
dbg!(alpha);
}
let crop_rect = geo.clone();
let mut elements =
AsRenderElements::<R>::render_elements::<CosmicMappedRenderElement<R>>(
mapped,
renderer,
loc.to_physical_precise_round(output_scale)
geo.loc.to_physical_precise_round(output_scale)
- mapped
.geometry()
.loc
.to_physical_precise_round(output_scale),
Scale::from(output_scale),
);
alpha,
)
.into_iter()
.flat_map(|element| match element {
CosmicMappedRenderElement::Stack(elem) => {
CropRenderElement::from_element(
elem,
output_scale,
crop_rect.to_physical_precise_round(output_scale),
)
.map(CosmicMappedRenderElement::CroppedStack)
}
CosmicMappedRenderElement::Window(elem) => {
CropRenderElement::from_element(
elem,
output_scale,
crop_rect.to_physical_precise_round(output_scale),
)
.map(CosmicMappedRenderElement::CroppedWindow)
}
x => Some(x),
})
.collect::<Vec<_>>();
if focused == Some(mapped) {
if indicator_thickness > 0 {
let element = IndicatorShader::element(
renderer,
Rectangle::from_loc_and_size(loc, mapped.geometry().size),
geo,
indicator_thickness,
1.0,
);
elements.insert(0, element.into());
}
}
elements
})
.collect::<Vec<_>>())
}),
)
}
Ok(elements)
}
}

View file

@ -1,3 +1,4 @@
use calloop::LoopHandle;
use serde::{Deserialize, Serialize};
use std::{cell::RefCell, collections::HashMap};
use tracing::warn;
@ -1036,6 +1037,18 @@ impl Shell {
}
}
pub fn animations_going(&self) -> bool {
self.workspaces
.spaces()
.any(|workspace| workspace.animations_going())
}
pub fn update_animations(&mut self, handle: &LoopHandle<'static, crate::state::Data>) {
for workspace in self.workspaces.spaces_mut() {
workspace.update_animations(handle)
}
}
pub fn refresh(&mut self) {
#[cfg(feature = "debug")]
puffin::profile_function!();

View file

@ -17,6 +17,7 @@ use crate::{
xwayland::XWaylandState,
};
use calloop::LoopHandle;
use indexmap::IndexSet;
use smithay::{
backend::renderer::{
@ -85,6 +86,14 @@ impl Workspace {
self.tiling_layer.refresh();
}
pub fn animations_going(&self) -> bool {
self.tiling_layer.animations_going()
}
pub fn update_animations(&mut self, handle: &LoopHandle<'static, crate::state::Data>) {
self.tiling_layer.update_animation_state(handle)
}
pub fn commit(&mut self, surface: &WlSurface) {
if let Some(mapped) = self.element_for_wl_surface(surface) {
mapped
@ -484,6 +493,7 @@ impl Workspace {
renderer,
loc.to_physical_precise_round(output_scale),
Scale::from(output_scale),
1.0,
)
}),
);
@ -499,6 +509,7 @@ impl Workspace {
(or.geometry().loc - output.geometry().loc)
.to_physical_precise_round(output_scale),
Scale::from(output_scale),
1.0,
)
}),
);
@ -507,7 +518,11 @@ impl Workspace {
render_elements.extend(AsRenderElements::<R>::render_elements::<
WorkspaceRenderElement<R>,
>(
fullscreen, renderer, (0, 0).into(), output_scale.into()
fullscreen,
renderer,
(0, 0).into(),
output_scale.into(),
1.0,
));
if let Some(xwm) = xwm_state.and_then(|state| state.xwm.as_mut()) {
@ -551,6 +566,7 @@ impl Workspace {
renderer,
loc.to_physical_precise_round(output_scale),
Scale::from(output_scale),
1.0,
)
}),
);
@ -570,6 +586,7 @@ impl Workspace {
(or.geometry().loc - output.geometry().loc)
.to_physical_precise_round(output_scale),
Scale::from(output_scale),
1.0,
)
}),
);
@ -620,6 +637,7 @@ impl Workspace {
renderer,
loc.to_physical_precise_round(output_scale),
Scale::from(output_scale),
1.0,
)
}),
);
@ -715,6 +733,13 @@ where
WorkspaceRenderElement::Window(elem) => elem.opaque_regions(scale),
}
}
fn alpha(&self) -> f32 {
match self {
WorkspaceRenderElement::Wayland(elem) => elem.alpha(),
WorkspaceRenderElement::Window(elem) => elem.alpha(),
}
}
}
impl<R> RenderElement<R> for WorkspaceRenderElement<R>

View file

@ -50,7 +50,7 @@ use smithay::{
},
utils::{Clock, IsAlive, Monotonic},
wayland::{
compositor::CompositorState,
compositor::{CompositorClientState, CompositorState},
data_device::DataDeviceState,
dmabuf::{DmabufFeedback, DmabufState},
keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitState,
@ -69,6 +69,7 @@ use std::{cell::RefCell, ffi::OsString, time::Duration};
use std::{collections::VecDeque, time::Instant};
pub struct ClientState {
pub compositor_client_state: CompositorClientState,
pub workspace_client_state: WorkspaceClientState,
pub drm_node: Option<DrmNode>,
pub privileged: bool,
@ -312,6 +313,7 @@ impl State {
pub fn new_client_state(&self) -> ClientState {
ClientState {
compositor_client_state: CompositorClientState::default(),
workspace_client_state: WorkspaceClientState::default(),
drm_node: match &self.backend {
BackendData::Kms(kms_state) => {
@ -334,6 +336,7 @@ impl State {
pub fn new_client_state_with_node(&self, drm_node: DrmNode) -> ClientState {
ClientState {
compositor_client_state: CompositorClientState::default(),
workspace_client_state: WorkspaceClientState::default(),
drm_node: Some(drm_node),
privileged: false,
@ -342,6 +345,7 @@ impl State {
pub fn new_privileged_client_state(&self) -> ClientState {
ClientState {
compositor_client_state: CompositorClientState::default(),
workspace_client_state: WorkspaceClientState::default(),
drm_node: match &self.backend {
BackendData::Kms(kms_state) => Some(kms_state.primary),

View file

@ -567,6 +567,7 @@ where
renderer: &mut R,
location: Point<i32, Physical>,
scale: Scale<f64>,
alpha: f32,
) -> Vec<C> {
let mut internal = self.0.lock().unwrap();
@ -633,7 +634,7 @@ where
renderer,
location.to_f64(),
&buffer,
None,
Some(alpha),
Some(Rectangle::from_loc_and_size(
(0., 0.),
size.to_f64().to_logical(1.0, Transform::Normal),

View file

@ -2,17 +2,22 @@
use crate::{
shell::CosmicSurface,
state::{BackendData, Data},
state::{BackendData, ClientState, Data},
utils::prelude::*,
wayland::protocols::screencopy::SessionType,
};
use calloop::Interest;
use smithay::{
backend::renderer::utils::{on_commit_buffer_handler, with_renderer_surface_state},
delegate_compositor,
desktop::{layer_map_for_output, LayerSurface, PopupKind, WindowSurfaceType},
reexports::wayland_server::protocol::wl_surface::WlSurface,
reexports::wayland_server::{protocol::wl_surface::WlSurface, Client, Resource},
wayland::{
compositor::{with_states, CompositorHandler, CompositorState},
compositor::{
add_blocker, add_pre_commit_hook, with_states, BufferAssignment, CompositorClientState,
CompositorHandler, CompositorState, SurfaceAttributes,
},
dmabuf::get_dmabuf,
seat::WaylandFocus,
shell::{
wlr_layer::LayerSurfaceAttributes,
@ -21,7 +26,7 @@ use smithay::{
},
},
},
xwayland::X11Wm,
xwayland::{X11Wm, XWaylandClientData},
};
use std::sync::Mutex;
@ -105,15 +110,63 @@ impl State {
}
}
pub fn client_compositor_state<'a>(client: &'a Client) -> &'a CompositorClientState {
if let Some(state) = client.get_data::<XWaylandClientData>() {
return &state.compositor_state;
}
if let Some(state) = client.get_data::<ClientState>() {
return &state.compositor_client_state;
}
panic!("Unknown client data type")
}
impl CompositorHandler for State {
fn compositor_state(&mut self) -> &mut CompositorState {
&mut self.common.compositor_state
}
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
client_compositor_state(client)
}
fn new_surface(&mut self, surface: &WlSurface) {
add_pre_commit_hook::<Self, _>(surface, move |state, _dh, surface| {
let maybe_dmabuf = with_states(surface, |surface_data| {
surface_data
.cached_state
.pending::<SurfaceAttributes>()
.buffer
.as_ref()
.and_then(|assignment| match assignment {
BufferAssignment::NewBuffer(buffer) => get_dmabuf(buffer).ok(),
_ => None,
})
});
if let Some(dmabuf) = maybe_dmabuf {
if let Ok((blocker, source)) = dmabuf.generate_blocker(Interest::READ) {
let client = surface.client().unwrap();
let res =
state
.common
.event_loop_handle
.insert_source(source, move |_, _, data| {
data.state
.client_compositor_state(&client)
.blocker_cleared(&mut data.state, &data.display.handle());
Ok(())
});
if res.is_ok() {
add_blocker(surface, blocker);
}
}
}
})
}
fn commit(&mut self, surface: &WlSurface) {
X11Wm::commit_hook::<Data>(surface);
// first load the buffer for various smithay helper functions
on_commit_buffer_handler(surface);
on_commit_buffer_handler::<Self>(surface);
// then handle initial configure events and map windows if necessary
if let Some((window, seat)) = self

View file

@ -947,6 +947,7 @@ pub fn render_window_to_buffer(
renderer,
(-geometry.loc.x, -geometry.loc.y).into(),
Scale::from(1.0),
1.0,
);
for seat in common.seats() {