From adc28eeb93552c086e25e28bbb3c59b34f51e0bd Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 19 May 2023 19:44:57 +0200 Subject: [PATCH] tiling: Animate and enable/disable hints --- src/backend/render/element.rs | 8 +- src/backend/render/mod.rs | 14 ++- src/config/mod.rs | 12 +- src/input/mod.rs | 89 +++++++++---- src/shell/element/mod.rs | 9 +- src/shell/element/window.rs | 7 +- src/shell/layout/floating/mod.rs | 5 +- src/shell/layout/tiling/mod.rs | 55 ++++++-- src/shell/mod.rs | 50 ++++++-- src/shell/workspace.rs | 196 +++++++++++++++++++++++++---- src/wayland/handlers/screencopy.rs | 7 +- 11 files changed, 363 insertions(+), 89 deletions(-) diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index d3bcbae8..07f5ad11 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -10,11 +10,9 @@ use smithay::{ }; #[cfg(feature = "debug")] -use smithay::backend::renderer::{ - element::texture::TextureRenderElement, gles::GlesTexture, multigpu::Error as MultiError, -}; +use smithay::backend::renderer::{element::texture::TextureRenderElement, gles::GlesTexture}; -use super::{cursor::CursorRenderElement, GlMultiFrame, GlMultiRenderer}; +use super::{cursor::CursorRenderElement, GlMultiError, GlMultiFrame, GlMultiRenderer}; pub enum CosmicElement where @@ -167,7 +165,7 @@ impl<'a, 'b> RenderElement> for CosmicElement, dst: Rectangle, damage: &[Rectangle], - ) -> Result<(), as Renderer>::Error> { + ) -> Result<(), GlMultiError> { match self { CosmicElement::Workspace(elem) => elem.draw(frame, src, dst, damage), CosmicElement::Cursor(elem) => elem.draw(frame, src, dst, damage), diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index dab84e90..1155acaf 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -16,6 +16,7 @@ use crate::{ shell::{ element::window::CosmicWindowRenderElement, focus::target::WindowGroup, layout::floating::SeatMoveGrabState, CosmicMapped, CosmicMappedRenderElement, + WorkspaceRenderElement, }, state::{Common, Fps}, utils::prelude::SeatExt, @@ -44,7 +45,7 @@ use smithay::{ UniformName, UniformType, }, glow::GlowRenderer, - multigpu::{gbm::GbmGlesBackend, MultiFrame, MultiRenderer}, + multigpu::{gbm::GbmGlesBackend, Error as MultiError, MultiFrame, MultiRenderer}, Bind, Blit, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, TextureFilter, }, }, @@ -66,6 +67,7 @@ pub type GlMultiRenderer<'a, 'b> = MultiRenderer<'a, 'a, 'b, GbmGlesBackend, GbmGlesBackend>; pub type GlMultiFrame<'a, 'b, 'frame> = MultiFrame<'a, 'a, 'b, 'frame, GbmGlesBackend, GbmGlesBackend>; +pub type GlMultiError = MultiError, GbmGlesBackend>; pub static CLEAR_COLOR: [f32; 4] = [0.153, 0.161, 0.165, 1.0]; pub static ACTIVE_GROUP_COLOR: [f32; 3] = [0.678, 0.635, 0.619]; @@ -298,6 +300,7 @@ where ::Error: From, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + WorkspaceRenderElement: RenderElement, { #[cfg(feature = "debug")] puffin::profile_function!(); @@ -346,6 +349,7 @@ where .space_for_handle_mut(&handle) .ok_or(OutputNoMode)? .update_animations(&state.event_loop_handle); + let overview = state.shell.overview_mode(); 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 @@ -365,7 +369,7 @@ where &state.shell.override_redirect_windows, state.xwayland_state.as_mut(), (!move_active && is_active_space).then_some(&last_active_seat), - true, + overview, state.config.static_conf.active_hint, exclude_workspace_overview, ) @@ -377,7 +381,7 @@ where Ok(elements) } -pub fn render_output<'frame, R, Target, OffTarget, Source>( +pub fn render_output( gpu: Option<&DrmNode>, renderer: &mut R, target: Target, @@ -404,6 +408,7 @@ where CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + WorkspaceRenderElement: RenderElement, Source: Clone, { let handle = state.shell.workspaces.active(output).handle; @@ -425,7 +430,7 @@ where result } -pub fn render_workspace<'frame, R, Target, OffTarget, Source>( +pub fn render_workspace( gpu: Option<&DrmNode>, renderer: &mut R, target: Target, @@ -454,6 +459,7 @@ where CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + WorkspaceRenderElement: RenderElement, Source: Clone, { #[cfg(feature = "debug")] diff --git a/src/config/mod.rs b/src/config/mod.rs index 25aff4c0..1feb854e 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -772,12 +772,12 @@ pub enum KeyModifier { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct KeyModifiers { - ctrl: bool, - alt: bool, - shift: bool, - logo: bool, - caps_lock: bool, - num_lock: bool, + pub ctrl: bool, + pub alt: bool, + pub shift: bool, + pub logo: bool, + pub caps_lock: bool, + pub num_lock: bool, } impl PartialEq for KeyModifiers { diff --git a/src/input/mod.rs b/src/input/mod.rs index cc171df1..93f9615f 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1,14 +1,14 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - config::{Action, Config}, + config::{Action, Config, KeyModifiers}, shell::{ focus::{target::PointerFocusTarget, FocusDirection}, layout::{ floating::SeatMoveGrabState, tiling::{Direction, FocusResult}, }, - Workspace, + OverviewMode, Workspace, }, // shell::grabs::SeatMoveGrabState state::Common, utils::prelude::*, @@ -219,7 +219,7 @@ impl State { let serial = SERIAL_COUNTER.next_serial(); let time = Event::time_msec(&event); - if let Some(action) = seat + if let Some((action, mods)) = seat .get_keyboard() .unwrap() .input( @@ -229,6 +229,18 @@ impl State { serial, time, |data, modifiers, handle| { + if let OverviewMode::Started(action_modifiers, _) = + data.common.shell.overview_mode() + { + if !(action_modifiers.ctrl && modifiers.ctrl) + && !(action_modifiers.alt && modifiers.alt) + && !(action_modifiers.logo && modifiers.logo) + && !(action_modifiers.shift && modifiers.shift) + { + data.common.shell.set_overview_mode(None); + } + } + if state == KeyState::Released && userdata.get::().unwrap().filter(&handle) { @@ -283,9 +295,10 @@ impl State { .get::() .unwrap() .add(&handle); - return FilterResult::Intercept(Some( + return FilterResult::Intercept(Some(( action.clone(), - )); + binding.modifiers.clone(), + ))); } } } @@ -295,7 +308,7 @@ impl State { ) .flatten() { - self.handle_action(action, seat, serial, time) + self.handle_action(action, seat, serial, time, mods) } break; } @@ -640,7 +653,14 @@ impl State { } } - fn handle_action(&mut self, action: Action, seat: &Seat, serial: Serial, time: u32) { + fn handle_action( + &mut self, + action: Action, + seat: &Seat, + serial: Serial, + time: u32, + mods: KeyModifiers, + ) { match action { Action::Terminate => { self.common.should_stop = true; @@ -920,17 +940,21 @@ impl State { FocusResult::None => { // TODO: Handle Workspace orientation match focus { - FocusDirection::Left => { - self.handle_action(Action::PreviousWorkspace, seat, serial, time) - } + FocusDirection::Left => self.handle_action( + Action::PreviousWorkspace, + seat, + serial, + time, + mods, + ), FocusDirection::Right => { - self.handle_action(Action::NextWorkspace, seat, serial, time) + self.handle_action(Action::NextWorkspace, seat, serial, time, mods) } FocusDirection::Up => { - self.handle_action(Action::PreviousOutput, seat, serial, time) + self.handle_action(Action::PreviousOutput, seat, serial, time, mods) } FocusDirection::Down => { - self.handle_action(Action::NextOutput, seat, serial, time) + self.handle_action(Action::NextOutput, seat, serial, time, mods) } _ => {} } @@ -955,17 +979,36 @@ impl State { // TODO: Handle Workspace orientation // TODO: Being able to move Groups (move_further should be KeyboardFocusTarget instead) match direction { - Direction::Left => { - self.handle_action(Action::MoveToPreviousWorkspace, seat, serial, time) - } - Direction::Right => { - self.handle_action(Action::MoveToNextWorkspace, seat, serial, time) - } - Direction::Up => { - self.handle_action(Action::MoveToPreviousOutput, seat, serial, time) - } + Direction::Left => self.handle_action( + Action::MoveToPreviousWorkspace, + seat, + serial, + time, + mods, + ), + Direction::Right => self.handle_action( + Action::MoveToNextWorkspace, + seat, + serial, + time, + mods, + ), + Direction::Up => self.handle_action( + Action::MoveToPreviousOutput, + seat, + serial, + time, + mods, + ), Direction::Down => { - self.handle_action(Action::MoveToNextOutput, seat, serial, time) + self.handle_action(Action::MoveToNextOutput, seat, serial, time, mods) + } + } + } else { + let focus_stack = workspace.focus_stack.get(seat); + if let Some(focused_window) = focus_stack.last() { + if workspace.is_tiled(focused_window) { + self.common.shell.set_overview_mode(Some(mods)); } } } diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 818d5a2a..dd4d1a72 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -1,7 +1,7 @@ use crate::{ backend::render::{ element::{AsGlowFrame, AsGlowRenderer}, - GlMultiFrame, GlMultiRenderer, + GlMultiError, GlMultiFrame, GlMultiRenderer, }, state::State, utils::prelude::SeatExt, @@ -17,7 +17,6 @@ use smithay::{ }, gles::element::PixelShaderElement, glow::GlowRenderer, - multigpu::Error as MultiError, ImportAll, ImportMem, Renderer, }, }, @@ -859,7 +858,7 @@ impl<'a, 'b> RenderElement> src: Rectangle, dst: Rectangle, damage: &[Rectangle], - ) -> Result<(), as Renderer>::Error> { + ) -> Result<(), GlMultiError> { match self { CosmicMappedRenderElement::Stack(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::Window(elem) => elem.draw(frame, src, dst, damage), @@ -867,13 +866,13 @@ impl<'a, 'b> RenderElement> CosmicMappedRenderElement::TiledWindow(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::Indicator(elem) => { RenderElement::::draw(elem, frame.glow_frame_mut(), src, dst, damage) - .map_err(|err| MultiError::Render(err)) + .map_err(|err| GlMultiError::Render(err)) } #[cfg(feature = "debug")] CosmicMappedRenderElement::Egui(elem) => { let glow_frame = frame.glow_frame_mut(); RenderElement::::draw(elem, glow_frame, src, dst, damage) - .map_err(|err| MultiError::Render(err)) + .map_err(|err| GlMultiError::Render(err)) } } } diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 35416758..c611be88 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -1,7 +1,7 @@ use crate::{ backend::render::{ element::{AsGlowFrame, AsGlowRenderer}, - GlMultiFrame, GlMultiRenderer, + GlMultiError, GlMultiFrame, GlMultiRenderer, }, shell::Shell, state::State, @@ -24,7 +24,6 @@ use smithay::{ AsRenderElements, Element, Id, RenderElement, }, glow::GlowRenderer, - multigpu::Error as MultiError, utils::CommitCounter, ImportAll, ImportMem, Renderer, }, @@ -683,11 +682,11 @@ impl<'a, 'b> RenderElement> src: Rectangle, dst: Rectangle, damage: &[Rectangle], - ) -> Result<(), as Renderer>::Error> { + ) -> Result<(), GlMultiError> { match self { CosmicWindowRenderElement::Header(h) => h .draw(frame.glow_frame_mut(), src, dst, damage) - .map_err(|err| MultiError::Render(err)), + .map_err(|err| GlMultiError::Render(err)), CosmicWindowRenderElement::Window(w) => w.draw(frame, src, dst, damage), } } diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index a82c2041..73c1b173 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -351,6 +351,7 @@ impl FloatingLayout { output: &Output, focused: Option<&CosmicMapped>, indicator_thickness: u8, + alpha: f32, ) -> Vec> where R: Renderer + ImportAll + ImportMem + AsGlowRenderer, @@ -374,7 +375,7 @@ impl FloatingLayout { renderer, render_location.to_physical_precise_round(output_scale), output_scale.into(), - 1.0, + alpha, ); if focused == Some(elem) { if indicator_thickness > 0 { @@ -386,7 +387,7 @@ impl FloatingLayout { elem.geometry().size, ), indicator_thickness, - 1.0, + alpha, FOCUS_INDICATOR_COLOR, ); elements.insert(0, element.into()); diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index d0d2d805..8a445c2c 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -13,7 +13,7 @@ use crate::{ }, grabs::ResizeEdge, layout::Orientation, - CosmicSurface, OutputNotMapped, + CosmicSurface, OutputNotMapped, OverviewMode, }, utils::prelude::*, wayland::{ @@ -52,7 +52,7 @@ mod grabs; pub use self::blocker::*; pub use self::grabs::*; -const ANIMATION_DURATION: Duration = Duration::from_millis(200); +pub const ANIMATION_DURATION: Duration = Duration::from_millis(200); #[derive(Debug, Clone)] struct OutputData { @@ -1439,7 +1439,7 @@ impl TilingLayout { output: &Output, focused: Option<&CosmicMapped>, non_exclusive_zone: Rectangle, - draw_groups: bool, + overview: OverviewMode, indicator_thickness: u8, ) -> Result>, OutputNotMapped> where @@ -1478,12 +1478,32 @@ impl TilingLayout { } else { 1.0 }; + let draw_groups = match overview { + OverviewMode::Started(_, start) => { + let percentage = (Instant::now().duration_since(start).as_millis() as f32 + / ANIMATION_DURATION.as_millis() as f32) + .min(1.0); + Some(Ease::Cubic(Cubic::Out).tween(percentage)) + } + OverviewMode::Ended(end) => { + let percentage = (1.0 + - Instant::now().duration_since(end).as_millis() as f32 + / ANIMATION_DURATION.as_millis() as f32) + .max(0.0); + if percentage > 0.0 { + Some(Ease::Cubic(Cubic::Out).tween(percentage)) + } else { + None + } + } + OverviewMode::None => None, + }; let mut elements = Vec::new(); // all gone windows and fade them out let old_geometries = if let Some(reference_tree) = reference_tree.as_ref() { - let (geometries, _) = if draw_groups { + let (geometries, _) = if let Some(transition) = draw_groups { geometries_for_groupview( reference_tree, renderer, @@ -1491,6 +1511,7 @@ impl TilingLayout { focused, // TODO: Would be better to be an old focus, // but for that we have to associate focus with a tree (and animate focus changes properly) 1.0 - percentage, + transition, ) } else { None @@ -1512,13 +1533,14 @@ impl TilingLayout { None }; - let (geometries, group_elements) = if draw_groups { + let (geometries, group_elements) = if let Some(transition) = draw_groups { geometries_for_groupview( target_tree, renderer, non_exclusive_zone, focused, percentage, + transition, ) } else { None @@ -1540,7 +1562,16 @@ impl TilingLayout { focused, output_scale, percentage, - if draw_groups { 3 } else { indicator_thickness }, + if let Some(transition) = draw_groups { + let diff = (3u8.abs_diff(indicator_thickness) as f32 * transition).round() as u8; + if 3 > indicator_thickness { + indicator_thickness + diff + } else { + indicator_thickness - diff + } + } else { + indicator_thickness + }, )); Ok(elements) @@ -1553,6 +1584,7 @@ fn geometries_for_groupview( non_exclusive_zone: Rectangle, focused: Option<&CosmicMapped>, alpha: f32, + transition: f32, ) -> Option<( HashMap>, Vec>, @@ -1570,11 +1602,14 @@ where let mut geometries = HashMap::new(); const GAP: i32 = 16; + let gap: i32 = (GAP as f32 * transition).round() as i32; + let alpha = alpha * transition; + for node_id in tree.traverse_pre_order_ids(root).unwrap() { if let Some(mut geo) = stack.pop() { // zoom in windows - geo.loc += (GAP, GAP).into(); - geo.size -= (GAP * 2, GAP * 2).into(); + geo.loc += (gap, gap).into(); + geo.size -= (gap * 2, gap * 2).into(); let node: &Node = tree.get(&node_id).unwrap(); let data = node.data(); @@ -1715,8 +1750,8 @@ where .into(), ); - geo.loc += (GAP, GAP).into(); - geo.size -= (GAP * 2, GAP * 2).into(); + geo.loc += (gap, gap).into(); + geo.size -= (gap * 2, gap * 2).into(); } geometries.insert(node_id.clone(), geo); diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 47f11785..2d704c20 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1,6 +1,6 @@ use calloop::LoopHandle; use serde::{Deserialize, Serialize}; -use std::{cell::RefCell, collections::HashMap}; +use std::{cell::RefCell, collections::HashMap, time::Instant}; use tracing::warn; use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::State as WState; @@ -29,7 +29,7 @@ use smithay::{ }; use crate::{ - config::{Config, OutputConfig, WorkspaceMode as ConfigMode}, + config::{Config, KeyModifiers, OutputConfig, WorkspaceMode as ConfigMode}, utils::prelude::*, wayland::protocols::{ toplevel_info::ToplevelInfoState, @@ -52,9 +52,19 @@ use self::{ element::CosmicWindow, focus::target::KeyboardFocusTarget, grabs::ResizeEdge, - layout::{floating::FloatingLayout, tiling::TilingLayout}, + layout::{ + floating::FloatingLayout, + tiling::{TilingLayout, ANIMATION_DURATION}, + }, }; +#[derive(Debug, Clone)] +pub enum OverviewMode { + None, + Started(KeyModifiers, Instant), + Ended(Instant), +} + pub struct Shell { pub popups: PopupManager, pub outputs: Vec, @@ -72,6 +82,7 @@ pub struct Shell { pub workspace_state: WorkspaceState, gaps: (u8, u8), + overview_mode: OverviewMode, } #[derive(Debug)] @@ -498,6 +509,7 @@ impl Shell { workspace_state, gaps: config.static_conf.gaps, + overview_mode: OverviewMode::None, } } @@ -1038,9 +1050,11 @@ impl Shell { } pub fn animations_going(&self) -> bool { - self.workspaces - .spaces() - .any(|workspace| workspace.animations_going()) + matches!(self.overview_mode, OverviewMode::None) + || self + .workspaces + .spaces() + .any(|workspace| workspace.animations_going()) } pub fn update_animations(&mut self, handle: &LoopHandle<'static, crate::state::Data>) { @@ -1049,6 +1063,28 @@ impl Shell { } } + pub fn set_overview_mode(&mut self, enabled: Option) { + if let Some(modifiers) = enabled { + if !matches!(self.overview_mode, OverviewMode::Started(_, _)) { + self.overview_mode = OverviewMode::Started(modifiers, Instant::now()); + } + } else { + if !matches!(self.overview_mode, OverviewMode::Ended(_)) { + self.overview_mode = OverviewMode::Ended(Instant::now()); + } + } + } + + pub fn overview_mode(&mut self) -> OverviewMode { + if let OverviewMode::Ended(timestamp) = self.overview_mode { + if Instant::now().duration_since(timestamp) > ANIMATION_DURATION { + self.overview_mode = OverviewMode::None; + } + } + + self.overview_mode.clone() + } + pub fn refresh(&mut self) { #[cfg(feature = "debug")] puffin::profile_function!(); @@ -1235,7 +1271,7 @@ impl Shell { } } let elements = from_workspace.mapped().cloned().collect::>(); - std::mem::drop(from_workspace); + for mapped in elements.into_iter() { state.common.shell.update_reactive_popups(&mapped); } diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index b5383f5b..f74b70f8 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1,8 +1,14 @@ use crate::{ - backend::render::element::AsGlowRenderer, - shell::layout::{ - floating::{FloatingLayout, MoveSurfaceGrab}, - tiling::TilingLayout, + backend::render::{ + element::{AsGlowFrame, AsGlowRenderer}, + GlMultiError, GlMultiFrame, GlMultiRenderer, + }, + shell::{ + layout::{ + floating::{FloatingLayout, MoveSurfaceGrab}, + tiling::{TilingLayout, ANIMATION_DURATION}, + }, + OverviewMode, }, state::State, utils::prelude::*, @@ -20,19 +26,30 @@ use crate::{ use calloop::LoopHandle; use indexmap::IndexSet; use smithay::{ - backend::renderer::{ - element::{surface::WaylandSurfaceRenderElement, AsRenderElements, Element, RenderElement}, - ImportAll, ImportMem, Renderer, + backend::{ + allocator::Fourcc, + renderer::{ + element::{ + surface::WaylandSurfaceRenderElement, texture::TextureRenderElement, + AsRenderElements, Element, Id, RenderElement, + }, + gles::{GlesError, GlesTexture}, + glow::{GlowFrame, GlowRenderer}, + ImportAll, ImportMem, Renderer, + }, }, desktop::{layer_map_for_output, space::SpaceElement, LayerSurface}, input::{pointer::GrabStartData as PointerGrabStartData, Seat}, output::Output, reexports::wayland_server::protocol::wl_surface::WlSurface, - utils::{Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Size}, + utils::{ + Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Size, + Transform, + }, wayland::{seat::WaylandFocus, shell::wlr_layer::Layer}, xwayland::X11Surface, }; -use std::collections::HashMap; +use std::{collections::HashMap, time::Instant}; use tracing::warn; use super::{ @@ -454,7 +471,7 @@ impl Workspace { override_redirect_windows: &[X11Surface], xwm_state: Option<&'a mut XWaylandState>, draw_focus_indicator: Option<&Seat>, - draw_groups: bool, + overview: OverviewMode, indicator_thickness: u8, exclude_workspace_overview: bool, ) -> Result>, OutputNotMapped> @@ -463,6 +480,7 @@ impl Workspace { ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + WorkspaceRenderElement: RenderElement, { #[cfg(feature = "debug")] puffin::profile_function!(); @@ -540,7 +558,7 @@ impl Workspace { } else { // TODO: Handle modes like // - keyboard window swapping - // - resizing / moving in tiling + // - resizing in tiling // overlay and top layer surfaces let lower = { @@ -595,9 +613,31 @@ impl Workspace { let focused = draw_focus_indicator.and_then(|seat| self.focus_stack.get(seat).last().cloned()); // floating surfaces + let alpha = match &overview { + OverviewMode::Started(_, started) => { + (1.0 - (Instant::now().duration_since(*started).as_millis() + / ANIMATION_DURATION.as_millis()) as f32) + .max(0.0) + * 0.4 + + 0.6 + } + OverviewMode::Ended(ended) => { + ((Instant::now().duration_since(*ended).as_millis() + / ANIMATION_DURATION.as_millis()) as f32) + * 0.4 + + 0.6 + } + OverviewMode::None => 1.0, + }; render_elements.extend( self.floating_layer - .render_output::(renderer, output, focused.as_ref(), indicator_thickness) + .render_output::( + renderer, + output, + focused.as_ref(), + indicator_thickness, + alpha, + ) .into_iter() .map(WorkspaceRenderElement::from), ); @@ -610,7 +650,7 @@ impl Workspace { output, focused.as_ref(), layer_map.non_exclusive_zone(), - draw_groups, + overview.clone(), indicator_thickness, )? .into_iter() @@ -629,6 +669,64 @@ impl Workspace { } } + if let OverviewMode::Started(_, start) = overview { + let alpha = Instant::now().duration_since(start).as_millis() as f64 / 100.0; + + #[derive(Clone)] + struct BackdropTexture(Id, GlesTexture); + + if renderer + .glow_renderer() + .egl_context() + .user_data() + .get::() + .is_none() + { + let tex = BackdropTexture( + Id::new(), + renderer + .glow_renderer_mut() + .import_memory(&[0, 0, 0, 255], Fourcc::Argb8888, (1, 1).into(), false) + .unwrap(), + ); + renderer + .glow_renderer() + .egl_context() + .user_data() + .insert_if_missing(|| tex); + }; + let BackdropTexture(id, tex) = renderer + .glow_renderer() + .egl_context() + .user_data() + .get::() + .unwrap() + .clone(); + + render_elements.push( + TextureRenderElement::from_static_texture( + id, + renderer.id(), + (0.0, 0.0), + tex, + 1, + smithay::utils::Transform::Normal, + Some(alpha as f32), + Some(Rectangle::from_loc_and_size((0., 0.), (1., 1.))), + Some(output.geometry().size), + if alpha >= 1.0 { + Some(vec![Rectangle::from_loc_and_size( + (0, 0), + output.geometry().size.to_buffer(1, Transform::Normal), + )]) + } else { + None + }, + ) + .into(), + ) + } + // bottom and background layer surfaces { render_elements.extend( @@ -675,6 +773,7 @@ where { Wayland(WaylandSurfaceRenderElement), Window(CosmicMappedRenderElement), + Backdrop(TextureRenderElement), } impl Element for WorkspaceRenderElement @@ -686,6 +785,7 @@ where match self { WorkspaceRenderElement::Wayland(elem) => elem.id(), WorkspaceRenderElement::Window(elem) => elem.id(), + WorkspaceRenderElement::Backdrop(elem) => elem.id(), } } @@ -693,6 +793,7 @@ where match self { WorkspaceRenderElement::Wayland(elem) => elem.current_commit(), WorkspaceRenderElement::Window(elem) => elem.current_commit(), + WorkspaceRenderElement::Backdrop(elem) => elem.current_commit(), } } @@ -700,6 +801,7 @@ where match self { WorkspaceRenderElement::Wayland(elem) => elem.src(), WorkspaceRenderElement::Window(elem) => elem.src(), + WorkspaceRenderElement::Backdrop(elem) => elem.src(), } } @@ -707,6 +809,7 @@ where match self { WorkspaceRenderElement::Wayland(elem) => elem.geometry(scale), WorkspaceRenderElement::Window(elem) => elem.geometry(scale), + WorkspaceRenderElement::Backdrop(elem) => elem.geometry(scale), } } @@ -714,6 +817,7 @@ where match self { WorkspaceRenderElement::Wayland(elem) => elem.location(scale), WorkspaceRenderElement::Window(elem) => elem.location(scale), + WorkspaceRenderElement::Backdrop(elem) => elem.location(scale), } } @@ -721,6 +825,7 @@ where match self { WorkspaceRenderElement::Wayland(elem) => elem.transform(), WorkspaceRenderElement::Window(elem) => elem.transform(), + WorkspaceRenderElement::Backdrop(elem) => elem.transform(), } } @@ -732,6 +837,7 @@ where match self { WorkspaceRenderElement::Wayland(elem) => elem.damage_since(scale, commit), WorkspaceRenderElement::Window(elem) => elem.damage_since(scale, commit), + WorkspaceRenderElement::Backdrop(elem) => elem.damage_since(scale, commit), } } @@ -739,6 +845,7 @@ where match self { WorkspaceRenderElement::Wayland(elem) => elem.opaque_regions(scale), WorkspaceRenderElement::Window(elem) => elem.opaque_regions(scale), + WorkspaceRenderElement::Backdrop(elem) => elem.opaque_regions(scale), } } @@ -746,36 +853,70 @@ where match self { WorkspaceRenderElement::Wayland(elem) => elem.alpha(), WorkspaceRenderElement::Window(elem) => elem.alpha(), + WorkspaceRenderElement::Backdrop(elem) => elem.alpha(), } } } -impl RenderElement for WorkspaceRenderElement -where - R: Renderer + ImportAll + ImportMem + AsGlowRenderer, - ::TextureId: 'static, - CosmicMappedRenderElement: RenderElement, -{ +impl RenderElement for WorkspaceRenderElement { fn draw<'frame>( &self, - frame: &mut ::Frame<'frame>, + frame: &mut GlowFrame<'frame>, src: Rectangle, dst: Rectangle, damage: &[Rectangle], - ) -> Result<(), ::Error> { + ) -> Result<(), GlesError> { match self { WorkspaceRenderElement::Wayland(elem) => elem.draw(frame, src, dst, damage), WorkspaceRenderElement::Window(elem) => elem.draw(frame, src, dst, damage), + WorkspaceRenderElement::Backdrop(elem) => { + RenderElement::::draw(elem, frame, src, dst, damage) + } } } fn underlying_storage( &self, - renderer: &mut R, + renderer: &mut GlowRenderer, ) -> Option { match self { WorkspaceRenderElement::Wayland(elem) => elem.underlying_storage(renderer), WorkspaceRenderElement::Window(elem) => elem.underlying_storage(renderer), + WorkspaceRenderElement::Backdrop(elem) => elem.underlying_storage(renderer), + } + } +} + +impl<'a, 'b> RenderElement> + for WorkspaceRenderElement> +{ + fn draw<'frame>( + &self, + frame: &mut GlMultiFrame<'a, 'b, 'frame>, + src: Rectangle, + dst: Rectangle, + damage: &[Rectangle], + ) -> Result<(), GlMultiError> { + match self { + WorkspaceRenderElement::Wayland(elem) => elem.draw(frame, src, dst, damage), + WorkspaceRenderElement::Window(elem) => elem.draw(frame, src, dst, damage), + WorkspaceRenderElement::Backdrop(elem) => { + RenderElement::::draw(elem, frame.glow_frame_mut(), src, dst, damage) + .map_err(GlMultiError::Render) + } + } + } + + fn underlying_storage( + &self, + renderer: &mut GlMultiRenderer<'a, 'b>, + ) -> Option { + match self { + WorkspaceRenderElement::Wayland(elem) => elem.underlying_storage(renderer), + WorkspaceRenderElement::Window(elem) => elem.underlying_storage(renderer), + WorkspaceRenderElement::Backdrop(elem) => { + elem.underlying_storage(renderer.glow_renderer_mut()) + } } } } @@ -801,3 +942,14 @@ where WorkspaceRenderElement::Window(elem) } } + +impl From> for WorkspaceRenderElement +where + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + ::TextureId: 'static, + CosmicMappedRenderElement: RenderElement, +{ + fn from(elem: TextureRenderElement) -> Self { + WorkspaceRenderElement::Backdrop(elem) + } +} diff --git a/src/wayland/handlers/screencopy.rs b/src/wayland/handlers/screencopy.rs index aebef716..467d9d9e 100644 --- a/src/wayland/handlers/screencopy.rs +++ b/src/wayland/handlers/screencopy.rs @@ -48,7 +48,10 @@ use crate::{ element::{AsGlowRenderer, CosmicElement}, render_output, render_workspace, CursorMode, CLEAR_COLOR, }, - shell::{element::window::CosmicWindowRenderElement, CosmicMappedRenderElement, CosmicSurface}, + shell::{ + element::window::CosmicWindowRenderElement, CosmicMappedRenderElement, CosmicSurface, + WorkspaceRenderElement, + }, state::{BackendData, ClientState, Common, Data, State}, utils::prelude::OutputExt, wayland::protocols::{ @@ -663,6 +666,7 @@ pub fn render_output_to_buffer( CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + WorkspaceRenderElement: RenderElement, { let cursor_mode = match session.cursor_mode() { ScreencopyCursorMode::Embedded => CursorMode::All, @@ -795,6 +799,7 @@ pub fn render_workspace_to_buffer( CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + WorkspaceRenderElement: RenderElement, { let cursor_mode = match session.cursor_mode() { ScreencopyCursorMode::Embedded => CursorMode::All,