tiling: Animate and enable/disable hints

This commit is contained in:
Victoria Brekenfeld 2023-05-19 19:44:57 +02:00
parent 4ea0136a9b
commit adc28eeb93
11 changed files with 363 additions and 89 deletions

View file

@ -10,11 +10,9 @@ use smithay::{
}; };
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
use smithay::backend::renderer::{ use smithay::backend::renderer::{element::texture::TextureRenderElement, gles::GlesTexture};
element::texture::TextureRenderElement, gles::GlesTexture, multigpu::Error as MultiError,
};
use super::{cursor::CursorRenderElement, GlMultiFrame, GlMultiRenderer}; use super::{cursor::CursorRenderElement, GlMultiError, GlMultiFrame, GlMultiRenderer};
pub enum CosmicElement<R> pub enum CosmicElement<R>
where where
@ -167,7 +165,7 @@ impl<'a, 'b> RenderElement<GlMultiRenderer<'a, 'b>> for CosmicElement<GlMultiRen
src: Rectangle<f64, BufferCoords>, src: Rectangle<f64, BufferCoords>,
dst: Rectangle<i32, Physical>, dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>], damage: &[Rectangle<i32, Physical>],
) -> Result<(), <GlMultiRenderer<'a, 'b> as Renderer>::Error> { ) -> Result<(), GlMultiError> {
match self { match self {
CosmicElement::Workspace(elem) => elem.draw(frame, src, dst, damage), CosmicElement::Workspace(elem) => elem.draw(frame, src, dst, damage),
CosmicElement::Cursor(elem) => elem.draw(frame, src, dst, damage), CosmicElement::Cursor(elem) => elem.draw(frame, src, dst, damage),

View file

@ -16,6 +16,7 @@ use crate::{
shell::{ shell::{
element::window::CosmicWindowRenderElement, focus::target::WindowGroup, element::window::CosmicWindowRenderElement, focus::target::WindowGroup,
layout::floating::SeatMoveGrabState, CosmicMapped, CosmicMappedRenderElement, layout::floating::SeatMoveGrabState, CosmicMapped, CosmicMappedRenderElement,
WorkspaceRenderElement,
}, },
state::{Common, Fps}, state::{Common, Fps},
utils::prelude::SeatExt, utils::prelude::SeatExt,
@ -44,7 +45,7 @@ use smithay::{
UniformName, UniformType, UniformName, UniformType,
}, },
glow::GlowRenderer, glow::GlowRenderer,
multigpu::{gbm::GbmGlesBackend, MultiFrame, MultiRenderer}, multigpu::{gbm::GbmGlesBackend, Error as MultiError, MultiFrame, MultiRenderer},
Bind, Blit, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, TextureFilter, Bind, Blit, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, TextureFilter,
}, },
}, },
@ -66,6 +67,7 @@ pub type GlMultiRenderer<'a, 'b> =
MultiRenderer<'a, 'a, 'b, GbmGlesBackend<GlowRenderer>, GbmGlesBackend<GlowRenderer>>; MultiRenderer<'a, 'a, 'b, GbmGlesBackend<GlowRenderer>, GbmGlesBackend<GlowRenderer>>;
pub type GlMultiFrame<'a, 'b, 'frame> = pub type GlMultiFrame<'a, 'b, 'frame> =
MultiFrame<'a, 'a, 'b, 'frame, GbmGlesBackend<GlowRenderer>, GbmGlesBackend<GlowRenderer>>; MultiFrame<'a, 'a, 'b, 'frame, GbmGlesBackend<GlowRenderer>, GbmGlesBackend<GlowRenderer>>;
pub type GlMultiError = MultiError<GbmGlesBackend<GlowRenderer>, GbmGlesBackend<GlowRenderer>>;
pub static CLEAR_COLOR: [f32; 4] = [0.153, 0.161, 0.165, 1.0]; 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]; pub static ACTIVE_GROUP_COLOR: [f32; 3] = [0.678, 0.635, 0.619];
@ -298,6 +300,7 @@ where
<R as Renderer>::Error: From<GlesError>, <R as Renderer>::Error: From<GlesError>,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
CosmicWindowRenderElement<R>: RenderElement<R>, CosmicWindowRenderElement<R>: RenderElement<R>,
WorkspaceRenderElement<R>: RenderElement<R>,
{ {
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
puffin::profile_function!(); puffin::profile_function!();
@ -346,6 +349,7 @@ where
.space_for_handle_mut(&handle) .space_for_handle_mut(&handle)
.ok_or(OutputNoMode)? .ok_or(OutputNoMode)?
.update_animations(&state.event_loop_handle); .update_animations(&state.event_loop_handle);
let overview = state.shell.overview_mode();
let workspace = state.shell.space_for_handle(&handle).ok_or(OutputNoMode)?; let workspace = state.shell.space_for_handle(&handle).ok_or(OutputNoMode)?;
let last_active_seat = state.last_active_seat().clone(); let last_active_seat = state.last_active_seat().clone();
let move_active = last_active_seat let move_active = last_active_seat
@ -365,7 +369,7 @@ where
&state.shell.override_redirect_windows, &state.shell.override_redirect_windows,
state.xwayland_state.as_mut(), state.xwayland_state.as_mut(),
(!move_active && is_active_space).then_some(&last_active_seat), (!move_active && is_active_space).then_some(&last_active_seat),
true, overview,
state.config.static_conf.active_hint, state.config.static_conf.active_hint,
exclude_workspace_overview, exclude_workspace_overview,
) )
@ -377,7 +381,7 @@ where
Ok(elements) Ok(elements)
} }
pub fn render_output<'frame, R, Target, OffTarget, Source>( pub fn render_output<R, Target, OffTarget, Source>(
gpu: Option<&DrmNode>, gpu: Option<&DrmNode>,
renderer: &mut R, renderer: &mut R,
target: Target, target: Target,
@ -404,6 +408,7 @@ where
CosmicElement<R>: RenderElement<R>, CosmicElement<R>: RenderElement<R>,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
CosmicWindowRenderElement<R>: RenderElement<R>, CosmicWindowRenderElement<R>: RenderElement<R>,
WorkspaceRenderElement<R>: RenderElement<R>,
Source: Clone, Source: Clone,
{ {
let handle = state.shell.workspaces.active(output).handle; let handle = state.shell.workspaces.active(output).handle;
@ -425,7 +430,7 @@ where
result result
} }
pub fn render_workspace<'frame, R, Target, OffTarget, Source>( pub fn render_workspace<R, Target, OffTarget, Source>(
gpu: Option<&DrmNode>, gpu: Option<&DrmNode>,
renderer: &mut R, renderer: &mut R,
target: Target, target: Target,
@ -454,6 +459,7 @@ where
CosmicElement<R>: RenderElement<R>, CosmicElement<R>: RenderElement<R>,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
CosmicWindowRenderElement<R>: RenderElement<R>, CosmicWindowRenderElement<R>: RenderElement<R>,
WorkspaceRenderElement<R>: RenderElement<R>,
Source: Clone, Source: Clone,
{ {
#[cfg(feature = "debug")] #[cfg(feature = "debug")]

View file

@ -772,12 +772,12 @@ pub enum KeyModifier {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct KeyModifiers { pub struct KeyModifiers {
ctrl: bool, pub ctrl: bool,
alt: bool, pub alt: bool,
shift: bool, pub shift: bool,
logo: bool, pub logo: bool,
caps_lock: bool, pub caps_lock: bool,
num_lock: bool, pub num_lock: bool,
} }
impl PartialEq<ModifiersState> for KeyModifiers { impl PartialEq<ModifiersState> for KeyModifiers {

View file

@ -1,14 +1,14 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::{ use crate::{
config::{Action, Config}, config::{Action, Config, KeyModifiers},
shell::{ shell::{
focus::{target::PointerFocusTarget, FocusDirection}, focus::{target::PointerFocusTarget, FocusDirection},
layout::{ layout::{
floating::SeatMoveGrabState, floating::SeatMoveGrabState,
tiling::{Direction, FocusResult}, tiling::{Direction, FocusResult},
}, },
Workspace, OverviewMode, Workspace,
}, // shell::grabs::SeatMoveGrabState }, // shell::grabs::SeatMoveGrabState
state::Common, state::Common,
utils::prelude::*, utils::prelude::*,
@ -219,7 +219,7 @@ impl State {
let serial = SERIAL_COUNTER.next_serial(); let serial = SERIAL_COUNTER.next_serial();
let time = Event::time_msec(&event); let time = Event::time_msec(&event);
if let Some(action) = seat if let Some((action, mods)) = seat
.get_keyboard() .get_keyboard()
.unwrap() .unwrap()
.input( .input(
@ -229,6 +229,18 @@ impl State {
serial, serial,
time, time,
|data, modifiers, handle| { |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 if state == KeyState::Released
&& userdata.get::<SupressedKeys>().unwrap().filter(&handle) && userdata.get::<SupressedKeys>().unwrap().filter(&handle)
{ {
@ -283,9 +295,10 @@ impl State {
.get::<SupressedKeys>() .get::<SupressedKeys>()
.unwrap() .unwrap()
.add(&handle); .add(&handle);
return FilterResult::Intercept(Some( return FilterResult::Intercept(Some((
action.clone(), action.clone(),
)); binding.modifiers.clone(),
)));
} }
} }
} }
@ -295,7 +308,7 @@ impl State {
) )
.flatten() .flatten()
{ {
self.handle_action(action, seat, serial, time) self.handle_action(action, seat, serial, time, mods)
} }
break; break;
} }
@ -640,7 +653,14 @@ impl State {
} }
} }
fn handle_action(&mut self, action: Action, seat: &Seat<State>, serial: Serial, time: u32) { fn handle_action(
&mut self,
action: Action,
seat: &Seat<State>,
serial: Serial,
time: u32,
mods: KeyModifiers,
) {
match action { match action {
Action::Terminate => { Action::Terminate => {
self.common.should_stop = true; self.common.should_stop = true;
@ -920,17 +940,21 @@ impl State {
FocusResult::None => { FocusResult::None => {
// TODO: Handle Workspace orientation // TODO: Handle Workspace orientation
match focus { match focus {
FocusDirection::Left => { FocusDirection::Left => self.handle_action(
self.handle_action(Action::PreviousWorkspace, seat, serial, time) Action::PreviousWorkspace,
} seat,
serial,
time,
mods,
),
FocusDirection::Right => { FocusDirection::Right => {
self.handle_action(Action::NextWorkspace, seat, serial, time) self.handle_action(Action::NextWorkspace, seat, serial, time, mods)
} }
FocusDirection::Up => { FocusDirection::Up => {
self.handle_action(Action::PreviousOutput, seat, serial, time) self.handle_action(Action::PreviousOutput, seat, serial, time, mods)
} }
FocusDirection::Down => { 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: Handle Workspace orientation
// TODO: Being able to move Groups (move_further should be KeyboardFocusTarget instead) // TODO: Being able to move Groups (move_further should be KeyboardFocusTarget instead)
match direction { match direction {
Direction::Left => { Direction::Left => self.handle_action(
self.handle_action(Action::MoveToPreviousWorkspace, seat, serial, time) Action::MoveToPreviousWorkspace,
} seat,
Direction::Right => { serial,
self.handle_action(Action::MoveToNextWorkspace, seat, serial, time) time,
} mods,
Direction::Up => { ),
self.handle_action(Action::MoveToPreviousOutput, seat, serial, time) Direction::Right => self.handle_action(
} Action::MoveToNextWorkspace,
seat,
serial,
time,
mods,
),
Direction::Up => self.handle_action(
Action::MoveToPreviousOutput,
seat,
serial,
time,
mods,
),
Direction::Down => { 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));
} }
} }
} }

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
backend::render::{ backend::render::{
element::{AsGlowFrame, AsGlowRenderer}, element::{AsGlowFrame, AsGlowRenderer},
GlMultiFrame, GlMultiRenderer, GlMultiError, GlMultiFrame, GlMultiRenderer,
}, },
state::State, state::State,
utils::prelude::SeatExt, utils::prelude::SeatExt,
@ -17,7 +17,6 @@ use smithay::{
}, },
gles::element::PixelShaderElement, gles::element::PixelShaderElement,
glow::GlowRenderer, glow::GlowRenderer,
multigpu::Error as MultiError,
ImportAll, ImportMem, Renderer, ImportAll, ImportMem, Renderer,
}, },
}, },
@ -859,7 +858,7 @@ impl<'a, 'b> RenderElement<GlMultiRenderer<'a, 'b>>
src: Rectangle<f64, BufferCoords>, src: Rectangle<f64, BufferCoords>,
dst: Rectangle<i32, Physical>, dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>], damage: &[Rectangle<i32, Physical>],
) -> Result<(), <GlMultiRenderer<'a, 'b> as Renderer>::Error> { ) -> Result<(), GlMultiError> {
match self { match self {
CosmicMappedRenderElement::Stack(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::Stack(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::Window(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::Window(elem) => elem.draw(frame, src, dst, damage),
@ -867,13 +866,13 @@ impl<'a, 'b> RenderElement<GlMultiRenderer<'a, 'b>>
CosmicMappedRenderElement::TiledWindow(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::TiledWindow(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::Indicator(elem) => { CosmicMappedRenderElement::Indicator(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame.glow_frame_mut(), src, dst, damage) RenderElement::<GlowRenderer>::draw(elem, frame.glow_frame_mut(), src, dst, damage)
.map_err(|err| MultiError::Render(err)) .map_err(|err| GlMultiError::Render(err))
} }
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => { CosmicMappedRenderElement::Egui(elem) => {
let glow_frame = frame.glow_frame_mut(); let glow_frame = frame.glow_frame_mut();
RenderElement::<GlowRenderer>::draw(elem, glow_frame, src, dst, damage) RenderElement::<GlowRenderer>::draw(elem, glow_frame, src, dst, damage)
.map_err(|err| MultiError::Render(err)) .map_err(|err| GlMultiError::Render(err))
} }
} }
} }

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
backend::render::{ backend::render::{
element::{AsGlowFrame, AsGlowRenderer}, element::{AsGlowFrame, AsGlowRenderer},
GlMultiFrame, GlMultiRenderer, GlMultiError, GlMultiFrame, GlMultiRenderer,
}, },
shell::Shell, shell::Shell,
state::State, state::State,
@ -24,7 +24,6 @@ use smithay::{
AsRenderElements, Element, Id, RenderElement, AsRenderElements, Element, Id, RenderElement,
}, },
glow::GlowRenderer, glow::GlowRenderer,
multigpu::Error as MultiError,
utils::CommitCounter, utils::CommitCounter,
ImportAll, ImportMem, Renderer, ImportAll, ImportMem, Renderer,
}, },
@ -683,11 +682,11 @@ impl<'a, 'b> RenderElement<GlMultiRenderer<'a, 'b>>
src: Rectangle<f64, smithay::utils::Buffer>, src: Rectangle<f64, smithay::utils::Buffer>,
dst: Rectangle<i32, Physical>, dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>], damage: &[Rectangle<i32, Physical>],
) -> Result<(), <GlMultiRenderer<'a, 'b> as Renderer>::Error> { ) -> Result<(), GlMultiError> {
match self { match self {
CosmicWindowRenderElement::Header(h) => h CosmicWindowRenderElement::Header(h) => h
.draw(frame.glow_frame_mut(), src, dst, damage) .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), CosmicWindowRenderElement::Window(w) => w.draw(frame, src, dst, damage),
} }
} }

View file

@ -351,6 +351,7 @@ impl FloatingLayout {
output: &Output, output: &Output,
focused: Option<&CosmicMapped>, focused: Option<&CosmicMapped>,
indicator_thickness: u8, indicator_thickness: u8,
alpha: f32,
) -> Vec<CosmicMappedRenderElement<R>> ) -> Vec<CosmicMappedRenderElement<R>>
where where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer, R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
@ -374,7 +375,7 @@ impl FloatingLayout {
renderer, renderer,
render_location.to_physical_precise_round(output_scale), render_location.to_physical_precise_round(output_scale),
output_scale.into(), output_scale.into(),
1.0, alpha,
); );
if focused == Some(elem) { if focused == Some(elem) {
if indicator_thickness > 0 { if indicator_thickness > 0 {
@ -386,7 +387,7 @@ impl FloatingLayout {
elem.geometry().size, elem.geometry().size,
), ),
indicator_thickness, indicator_thickness,
1.0, alpha,
FOCUS_INDICATOR_COLOR, FOCUS_INDICATOR_COLOR,
); );
elements.insert(0, element.into()); elements.insert(0, element.into());

View file

@ -13,7 +13,7 @@ use crate::{
}, },
grabs::ResizeEdge, grabs::ResizeEdge,
layout::Orientation, layout::Orientation,
CosmicSurface, OutputNotMapped, CosmicSurface, OutputNotMapped, OverviewMode,
}, },
utils::prelude::*, utils::prelude::*,
wayland::{ wayland::{
@ -52,7 +52,7 @@ mod grabs;
pub use self::blocker::*; pub use self::blocker::*;
pub use self::grabs::*; pub use self::grabs::*;
const ANIMATION_DURATION: Duration = Duration::from_millis(200); pub const ANIMATION_DURATION: Duration = Duration::from_millis(200);
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct OutputData { struct OutputData {
@ -1439,7 +1439,7 @@ impl TilingLayout {
output: &Output, output: &Output,
focused: Option<&CosmicMapped>, focused: Option<&CosmicMapped>,
non_exclusive_zone: Rectangle<i32, Logical>, non_exclusive_zone: Rectangle<i32, Logical>,
draw_groups: bool, overview: OverviewMode,
indicator_thickness: u8, indicator_thickness: u8,
) -> Result<Vec<CosmicMappedRenderElement<R>>, OutputNotMapped> ) -> Result<Vec<CosmicMappedRenderElement<R>>, OutputNotMapped>
where where
@ -1478,12 +1478,32 @@ impl TilingLayout {
} else { } else {
1.0 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(); let mut elements = Vec::new();
// all gone windows and fade them out // all gone windows and fade them out
let old_geometries = if let Some(reference_tree) = reference_tree.as_ref() { 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( geometries_for_groupview(
reference_tree, reference_tree,
renderer, renderer,
@ -1491,6 +1511,7 @@ impl TilingLayout {
focused, // TODO: Would be better to be an old focus, 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) // but for that we have to associate focus with a tree (and animate focus changes properly)
1.0 - percentage, 1.0 - percentage,
transition,
) )
} else { } else {
None None
@ -1512,13 +1533,14 @@ impl TilingLayout {
None None
}; };
let (geometries, group_elements) = if draw_groups { let (geometries, group_elements) = if let Some(transition) = draw_groups {
geometries_for_groupview( geometries_for_groupview(
target_tree, target_tree,
renderer, renderer,
non_exclusive_zone, non_exclusive_zone,
focused, focused,
percentage, percentage,
transition,
) )
} else { } else {
None None
@ -1540,7 +1562,16 @@ impl TilingLayout {
focused, focused,
output_scale, output_scale,
percentage, 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) Ok(elements)
@ -1553,6 +1584,7 @@ fn geometries_for_groupview<R>(
non_exclusive_zone: Rectangle<i32, Logical>, non_exclusive_zone: Rectangle<i32, Logical>,
focused: Option<&CosmicMapped>, focused: Option<&CosmicMapped>,
alpha: f32, alpha: f32,
transition: f32,
) -> Option<( ) -> Option<(
HashMap<NodeId, Rectangle<i32, Logical>>, HashMap<NodeId, Rectangle<i32, Logical>>,
Vec<CosmicMappedRenderElement<R>>, Vec<CosmicMappedRenderElement<R>>,
@ -1570,11 +1602,14 @@ where
let mut geometries = HashMap::new(); let mut geometries = HashMap::new();
const GAP: i32 = 16; 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() { for node_id in tree.traverse_pre_order_ids(root).unwrap() {
if let Some(mut geo) = stack.pop() { if let Some(mut geo) = stack.pop() {
// zoom in windows // zoom in windows
geo.loc += (GAP, GAP).into(); geo.loc += (gap, gap).into();
geo.size -= (GAP * 2, GAP * 2).into(); geo.size -= (gap * 2, gap * 2).into();
let node: &Node<Data> = tree.get(&node_id).unwrap(); let node: &Node<Data> = tree.get(&node_id).unwrap();
let data = node.data(); let data = node.data();
@ -1715,8 +1750,8 @@ where
.into(), .into(),
); );
geo.loc += (GAP, GAP).into(); geo.loc += (gap, gap).into();
geo.size -= (GAP * 2, GAP * 2).into(); geo.size -= (gap * 2, gap * 2).into();
} }
geometries.insert(node_id.clone(), geo); geometries.insert(node_id.clone(), geo);

View file

@ -1,6 +1,6 @@
use calloop::LoopHandle; use calloop::LoopHandle;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{cell::RefCell, collections::HashMap}; use std::{cell::RefCell, collections::HashMap, time::Instant};
use tracing::warn; use tracing::warn;
use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::State as WState; use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::State as WState;
@ -29,7 +29,7 @@ use smithay::{
}; };
use crate::{ use crate::{
config::{Config, OutputConfig, WorkspaceMode as ConfigMode}, config::{Config, KeyModifiers, OutputConfig, WorkspaceMode as ConfigMode},
utils::prelude::*, utils::prelude::*,
wayland::protocols::{ wayland::protocols::{
toplevel_info::ToplevelInfoState, toplevel_info::ToplevelInfoState,
@ -52,9 +52,19 @@ use self::{
element::CosmicWindow, element::CosmicWindow,
focus::target::KeyboardFocusTarget, focus::target::KeyboardFocusTarget,
grabs::ResizeEdge, 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 struct Shell {
pub popups: PopupManager, pub popups: PopupManager,
pub outputs: Vec<Output>, pub outputs: Vec<Output>,
@ -72,6 +82,7 @@ pub struct Shell {
pub workspace_state: WorkspaceState<State>, pub workspace_state: WorkspaceState<State>,
gaps: (u8, u8), gaps: (u8, u8),
overview_mode: OverviewMode,
} }
#[derive(Debug)] #[derive(Debug)]
@ -498,6 +509,7 @@ impl Shell {
workspace_state, workspace_state,
gaps: config.static_conf.gaps, gaps: config.static_conf.gaps,
overview_mode: OverviewMode::None,
} }
} }
@ -1038,9 +1050,11 @@ impl Shell {
} }
pub fn animations_going(&self) -> bool { pub fn animations_going(&self) -> bool {
self.workspaces matches!(self.overview_mode, OverviewMode::None)
.spaces() || self
.any(|workspace| workspace.animations_going()) .workspaces
.spaces()
.any(|workspace| workspace.animations_going())
} }
pub fn update_animations(&mut self, handle: &LoopHandle<'static, crate::state::Data>) { 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<KeyModifiers>) {
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) { pub fn refresh(&mut self) {
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
puffin::profile_function!(); puffin::profile_function!();
@ -1235,7 +1271,7 @@ impl Shell {
} }
} }
let elements = from_workspace.mapped().cloned().collect::<Vec<_>>(); let elements = from_workspace.mapped().cloned().collect::<Vec<_>>();
std::mem::drop(from_workspace);
for mapped in elements.into_iter() { for mapped in elements.into_iter() {
state.common.shell.update_reactive_popups(&mapped); state.common.shell.update_reactive_popups(&mapped);
} }

View file

@ -1,8 +1,14 @@
use crate::{ use crate::{
backend::render::element::AsGlowRenderer, backend::render::{
shell::layout::{ element::{AsGlowFrame, AsGlowRenderer},
floating::{FloatingLayout, MoveSurfaceGrab}, GlMultiError, GlMultiFrame, GlMultiRenderer,
tiling::TilingLayout, },
shell::{
layout::{
floating::{FloatingLayout, MoveSurfaceGrab},
tiling::{TilingLayout, ANIMATION_DURATION},
},
OverviewMode,
}, },
state::State, state::State,
utils::prelude::*, utils::prelude::*,
@ -20,19 +26,30 @@ use crate::{
use calloop::LoopHandle; use calloop::LoopHandle;
use indexmap::IndexSet; use indexmap::IndexSet;
use smithay::{ use smithay::{
backend::renderer::{ backend::{
element::{surface::WaylandSurfaceRenderElement, AsRenderElements, Element, RenderElement}, allocator::Fourcc,
ImportAll, ImportMem, Renderer, 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}, desktop::{layer_map_for_output, space::SpaceElement, LayerSurface},
input::{pointer::GrabStartData as PointerGrabStartData, Seat}, input::{pointer::GrabStartData as PointerGrabStartData, Seat},
output::Output, output::Output,
reexports::wayland_server::protocol::wl_surface::WlSurface, 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}, wayland::{seat::WaylandFocus, shell::wlr_layer::Layer},
xwayland::X11Surface, xwayland::X11Surface,
}; };
use std::collections::HashMap; use std::{collections::HashMap, time::Instant};
use tracing::warn; use tracing::warn;
use super::{ use super::{
@ -454,7 +471,7 @@ impl Workspace {
override_redirect_windows: &[X11Surface], override_redirect_windows: &[X11Surface],
xwm_state: Option<&'a mut XWaylandState>, xwm_state: Option<&'a mut XWaylandState>,
draw_focus_indicator: Option<&Seat<State>>, draw_focus_indicator: Option<&Seat<State>>,
draw_groups: bool, overview: OverviewMode,
indicator_thickness: u8, indicator_thickness: u8,
exclude_workspace_overview: bool, exclude_workspace_overview: bool,
) -> Result<Vec<WorkspaceRenderElement<R>>, OutputNotMapped> ) -> Result<Vec<WorkspaceRenderElement<R>>, OutputNotMapped>
@ -463,6 +480,7 @@ impl Workspace {
<R as Renderer>::TextureId: 'static, <R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
CosmicWindowRenderElement<R>: RenderElement<R>, CosmicWindowRenderElement<R>: RenderElement<R>,
WorkspaceRenderElement<R>: RenderElement<R>,
{ {
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
puffin::profile_function!(); puffin::profile_function!();
@ -540,7 +558,7 @@ impl Workspace {
} else { } else {
// TODO: Handle modes like // TODO: Handle modes like
// - keyboard window swapping // - keyboard window swapping
// - resizing / moving in tiling // - resizing in tiling
// overlay and top layer surfaces // overlay and top layer surfaces
let lower = { let lower = {
@ -595,9 +613,31 @@ impl Workspace {
let focused = let focused =
draw_focus_indicator.and_then(|seat| self.focus_stack.get(seat).last().cloned()); draw_focus_indicator.and_then(|seat| self.focus_stack.get(seat).last().cloned());
// floating surfaces // 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( render_elements.extend(
self.floating_layer self.floating_layer
.render_output::<R>(renderer, output, focused.as_ref(), indicator_thickness) .render_output::<R>(
renderer,
output,
focused.as_ref(),
indicator_thickness,
alpha,
)
.into_iter() .into_iter()
.map(WorkspaceRenderElement::from), .map(WorkspaceRenderElement::from),
); );
@ -610,7 +650,7 @@ impl Workspace {
output, output,
focused.as_ref(), focused.as_ref(),
layer_map.non_exclusive_zone(), layer_map.non_exclusive_zone(),
draw_groups, overview.clone(),
indicator_thickness, indicator_thickness,
)? )?
.into_iter() .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::<BackdropTexture>()
.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::<BackdropTexture>()
.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 // bottom and background layer surfaces
{ {
render_elements.extend( render_elements.extend(
@ -675,6 +773,7 @@ where
{ {
Wayland(WaylandSurfaceRenderElement<R>), Wayland(WaylandSurfaceRenderElement<R>),
Window(CosmicMappedRenderElement<R>), Window(CosmicMappedRenderElement<R>),
Backdrop(TextureRenderElement<GlesTexture>),
} }
impl<R> Element for WorkspaceRenderElement<R> impl<R> Element for WorkspaceRenderElement<R>
@ -686,6 +785,7 @@ where
match self { match self {
WorkspaceRenderElement::Wayland(elem) => elem.id(), WorkspaceRenderElement::Wayland(elem) => elem.id(),
WorkspaceRenderElement::Window(elem) => elem.id(), WorkspaceRenderElement::Window(elem) => elem.id(),
WorkspaceRenderElement::Backdrop(elem) => elem.id(),
} }
} }
@ -693,6 +793,7 @@ where
match self { match self {
WorkspaceRenderElement::Wayland(elem) => elem.current_commit(), WorkspaceRenderElement::Wayland(elem) => elem.current_commit(),
WorkspaceRenderElement::Window(elem) => elem.current_commit(), WorkspaceRenderElement::Window(elem) => elem.current_commit(),
WorkspaceRenderElement::Backdrop(elem) => elem.current_commit(),
} }
} }
@ -700,6 +801,7 @@ where
match self { match self {
WorkspaceRenderElement::Wayland(elem) => elem.src(), WorkspaceRenderElement::Wayland(elem) => elem.src(),
WorkspaceRenderElement::Window(elem) => elem.src(), WorkspaceRenderElement::Window(elem) => elem.src(),
WorkspaceRenderElement::Backdrop(elem) => elem.src(),
} }
} }
@ -707,6 +809,7 @@ where
match self { match self {
WorkspaceRenderElement::Wayland(elem) => elem.geometry(scale), WorkspaceRenderElement::Wayland(elem) => elem.geometry(scale),
WorkspaceRenderElement::Window(elem) => elem.geometry(scale), WorkspaceRenderElement::Window(elem) => elem.geometry(scale),
WorkspaceRenderElement::Backdrop(elem) => elem.geometry(scale),
} }
} }
@ -714,6 +817,7 @@ where
match self { match self {
WorkspaceRenderElement::Wayland(elem) => elem.location(scale), WorkspaceRenderElement::Wayland(elem) => elem.location(scale),
WorkspaceRenderElement::Window(elem) => elem.location(scale), WorkspaceRenderElement::Window(elem) => elem.location(scale),
WorkspaceRenderElement::Backdrop(elem) => elem.location(scale),
} }
} }
@ -721,6 +825,7 @@ where
match self { match self {
WorkspaceRenderElement::Wayland(elem) => elem.transform(), WorkspaceRenderElement::Wayland(elem) => elem.transform(),
WorkspaceRenderElement::Window(elem) => elem.transform(), WorkspaceRenderElement::Window(elem) => elem.transform(),
WorkspaceRenderElement::Backdrop(elem) => elem.transform(),
} }
} }
@ -732,6 +837,7 @@ where
match self { match self {
WorkspaceRenderElement::Wayland(elem) => elem.damage_since(scale, commit), WorkspaceRenderElement::Wayland(elem) => elem.damage_since(scale, commit),
WorkspaceRenderElement::Window(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 { match self {
WorkspaceRenderElement::Wayland(elem) => elem.opaque_regions(scale), WorkspaceRenderElement::Wayland(elem) => elem.opaque_regions(scale),
WorkspaceRenderElement::Window(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 { match self {
WorkspaceRenderElement::Wayland(elem) => elem.alpha(), WorkspaceRenderElement::Wayland(elem) => elem.alpha(),
WorkspaceRenderElement::Window(elem) => elem.alpha(), WorkspaceRenderElement::Window(elem) => elem.alpha(),
WorkspaceRenderElement::Backdrop(elem) => elem.alpha(),
} }
} }
} }
impl<R> RenderElement<R> for WorkspaceRenderElement<R> impl RenderElement<GlowRenderer> for WorkspaceRenderElement<GlowRenderer> {
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
{
fn draw<'frame>( fn draw<'frame>(
&self, &self,
frame: &mut <R as Renderer>::Frame<'frame>, frame: &mut GlowFrame<'frame>,
src: Rectangle<f64, BufferCoords>, src: Rectangle<f64, BufferCoords>,
dst: Rectangle<i32, Physical>, dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, smithay::utils::Physical>], damage: &[Rectangle<i32, smithay::utils::Physical>],
) -> Result<(), <R as Renderer>::Error> { ) -> Result<(), GlesError> {
match self { match self {
WorkspaceRenderElement::Wayland(elem) => elem.draw(frame, src, dst, damage), WorkspaceRenderElement::Wayland(elem) => elem.draw(frame, src, dst, damage),
WorkspaceRenderElement::Window(elem) => elem.draw(frame, src, dst, damage), WorkspaceRenderElement::Window(elem) => elem.draw(frame, src, dst, damage),
WorkspaceRenderElement::Backdrop(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage)
}
} }
} }
fn underlying_storage( fn underlying_storage(
&self, &self,
renderer: &mut R, renderer: &mut GlowRenderer,
) -> Option<smithay::backend::renderer::element::UnderlyingStorage> { ) -> Option<smithay::backend::renderer::element::UnderlyingStorage> {
match self { match self {
WorkspaceRenderElement::Wayland(elem) => elem.underlying_storage(renderer), WorkspaceRenderElement::Wayland(elem) => elem.underlying_storage(renderer),
WorkspaceRenderElement::Window(elem) => elem.underlying_storage(renderer), WorkspaceRenderElement::Window(elem) => elem.underlying_storage(renderer),
WorkspaceRenderElement::Backdrop(elem) => elem.underlying_storage(renderer),
}
}
}
impl<'a, 'b> RenderElement<GlMultiRenderer<'a, 'b>>
for WorkspaceRenderElement<GlMultiRenderer<'a, 'b>>
{
fn draw<'frame>(
&self,
frame: &mut GlMultiFrame<'a, 'b, 'frame>,
src: Rectangle<f64, BufferCoords>,
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, smithay::utils::Physical>],
) -> 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::<GlowRenderer>::draw(elem, frame.glow_frame_mut(), src, dst, damage)
.map_err(GlMultiError::Render)
}
}
}
fn underlying_storage(
&self,
renderer: &mut GlMultiRenderer<'a, 'b>,
) -> Option<smithay::backend::renderer::element::UnderlyingStorage> {
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) WorkspaceRenderElement::Window(elem)
} }
} }
impl<R> From<TextureRenderElement<GlesTexture>> for WorkspaceRenderElement<R>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
{
fn from(elem: TextureRenderElement<GlesTexture>) -> Self {
WorkspaceRenderElement::Backdrop(elem)
}
}

View file

@ -48,7 +48,10 @@ use crate::{
element::{AsGlowRenderer, CosmicElement}, element::{AsGlowRenderer, CosmicElement},
render_output, render_workspace, CursorMode, CLEAR_COLOR, 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}, state::{BackendData, ClientState, Common, Data, State},
utils::prelude::OutputExt, utils::prelude::OutputExt,
wayland::protocols::{ wayland::protocols::{
@ -663,6 +666,7 @@ pub fn render_output_to_buffer(
CosmicElement<R>: RenderElement<R>, CosmicElement<R>: RenderElement<R>,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
CosmicWindowRenderElement<R>: RenderElement<R>, CosmicWindowRenderElement<R>: RenderElement<R>,
WorkspaceRenderElement<R>: RenderElement<R>,
{ {
let cursor_mode = match session.cursor_mode() { let cursor_mode = match session.cursor_mode() {
ScreencopyCursorMode::Embedded => CursorMode::All, ScreencopyCursorMode::Embedded => CursorMode::All,
@ -795,6 +799,7 @@ pub fn render_workspace_to_buffer(
CosmicElement<R>: RenderElement<R>, CosmicElement<R>: RenderElement<R>,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
CosmicWindowRenderElement<R>: RenderElement<R>, CosmicWindowRenderElement<R>: RenderElement<R>,
WorkspaceRenderElement<R>: RenderElement<R>,
{ {
let cursor_mode = match session.cursor_mode() { let cursor_mode = match session.cursor_mode() {
ScreencopyCursorMode::Embedded => CursorMode::All, ScreencopyCursorMode::Embedded => CursorMode::All,