tiling: Animate tree changes
This commit is contained in:
parent
ea1b976076
commit
331b884f1e
23 changed files with 1641 additions and 395 deletions
959
Cargo.lock
generated
959
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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" }
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ impl WinitState {
|
|||
.unwrap_or_default(),
|
||||
0,
|
||||
wp_presentation_feedback::Kind::Vsync,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -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!(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
65
src/shell/layout/tiling/blocker.rs
Normal file
65
src/shell/layout/tiling/blocker.rs
Normal 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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!();
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue