cosmic-comp/src/shell/workspace.rs

1209 lines
42 KiB
Rust
Raw Normal View History

use crate::{
backend::render::{
element::{AsGlowFrame, AsGlowRenderer},
2023-05-26 20:51:10 +02:00
BackdropShader, GlMultiError, GlMultiFrame, GlMultiRenderer,
},
shell::{
2023-09-15 18:37:34 +02:00
layout::{floating::FloatingLayout, tiling::TilingLayout},
OverviewMode, ANIMATION_DURATION,
2022-09-28 12:01:29 +02:00
},
state::State,
2023-09-15 18:37:34 +02:00
utils::{prelude::*, tween::EaseRectangle},
2022-11-03 18:51:27 +01:00
wayland::{
handlers::screencopy::DropableSession,
2022-11-03 18:51:27 +01:00
protocols::{
screencopy::{BufferParams, Session as ScreencopySession},
toplevel_info::ToplevelInfoState,
2024-01-03 17:11:50 +00:00
workspace::{WorkspaceHandle, WorkspaceUpdateGuard},
2022-11-03 18:51:27 +01:00
},
},
};
use cosmic::theme::CosmicTheme;
2024-01-03 17:11:50 +00:00
use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::TilingState;
use id_tree::Tree;
2022-09-28 12:01:29 +02:00
use indexmap::IndexSet;
2023-09-15 18:37:34 +02:00
use keyframe::{ease, functions::EaseInOutCubic};
2022-03-24 20:32:31 +01:00
use smithay::{
2023-05-26 20:51:10 +02:00
backend::renderer::{
element::{
2023-09-15 18:37:34 +02:00
surface::WaylandSurfaceRenderElement, texture::TextureRenderElement,
utils::RescaleRenderElement, Element, Id, RenderElement,
},
2023-05-26 20:51:10 +02:00
gles::{GlesError, GlesTexture},
glow::{GlowFrame, GlowRenderer},
ImportAll, ImportMem, Renderer,
2022-09-28 12:01:29 +02:00
},
2023-09-15 18:37:34 +02:00
desktop::{layer_map_for_output, space::SpaceElement},
input::Seat,
2022-11-03 18:51:27 +01:00
output::Output,
reexports::wayland_server::{protocol::wl_surface::WlSurface, Client, Resource},
2023-05-22 16:55:29 +02:00
utils::{Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Size},
2023-09-15 18:37:34 +02:00
wayland::{
compositor::{add_blocker, Blocker, BlockerState},
seat::WaylandFocus,
2023-11-07 18:46:25 +01:00
xdg_activation::{XdgActivationState, XdgActivationToken},
2023-09-15 18:37:34 +02:00
},
2022-09-28 12:01:29 +02:00
};
2023-09-15 18:37:34 +02:00
use std::{
2023-11-07 18:46:25 +01:00
collections::{HashMap, HashSet, VecDeque},
2023-09-15 18:37:34 +02:00
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
time::{Duration, Instant},
};
use wayland_backend::server::ClientId;
2022-09-28 12:01:29 +02:00
use super::{
2023-07-06 00:03:26 +02:00
element::{
resize_indicator::ResizeIndicator, stack::CosmicStackRenderElement,
swap_indicator::SwapIndicator, window::CosmicWindowRenderElement, CosmicMapped,
2023-07-06 00:03:26 +02:00
},
focus::{
target::{KeyboardFocusTarget, PointerFocusTarget, WindowGroup},
FocusStack, FocusStackMut,
},
grabs::ResizeEdge,
layout::tiling::{Data, NodeDesc},
CosmicMappedRenderElement, CosmicSurface, ResizeDirection, ResizeMode,
2022-03-24 20:32:31 +01:00
};
2022-03-30 22:00:44 +02:00
2023-09-15 18:37:34 +02:00
const FULLSCREEN_ANIMATION_DURATION: Duration = Duration::from_millis(200);
2022-09-28 12:01:29 +02:00
#[derive(Debug)]
2022-03-24 20:32:31 +01:00
pub struct Workspace {
2023-10-25 19:40:26 +02:00
pub output: Output,
pub tiling_layer: TilingLayout,
pub floating_layer: FloatingLayout,
2022-09-28 12:01:29 +02:00
pub tiling_enabled: bool,
2023-10-25 19:41:30 +02:00
pub fullscreen: Option<FullscreenSurface>,
pub handle: WorkspaceHandle,
2022-09-28 12:01:29 +02:00
pub focus_stack: FocusStacks,
2022-11-03 18:51:27 +01:00
pub pending_buffers: Vec<(ScreencopySession, BufferParams)>,
pub screencopy_sessions: Vec<DropableSession>,
2023-10-25 19:40:26 +02:00
pub output_stack: VecDeque<String>,
2023-11-07 18:46:25 +01:00
pub pending_tokens: HashSet<XdgActivationToken>,
2023-05-26 20:51:10 +02:00
pub(super) backdrop_id: Id,
pub dirty: AtomicBool,
2022-03-24 20:32:31 +01:00
}
2023-09-13 20:14:54 +02:00
#[derive(Debug, Clone)]
pub struct FullscreenSurface {
2023-10-25 19:41:30 +02:00
pub surface: CosmicSurface,
pub previously: Option<(ManagedLayer, WorkspaceHandle)>,
2023-10-25 19:41:30 +02:00
original_geometry: Rectangle<i32, Global>,
2023-09-15 18:37:34 +02:00
start_at: Option<Instant>,
ended_at: Option<Instant>,
animation_signal: Option<Arc<AtomicBool>>,
}
2023-10-25 19:41:30 +02:00
impl PartialEq for FullscreenSurface {
fn eq(&self, other: &Self) -> bool {
self.surface == other.surface
}
}
2023-09-15 18:37:34 +02:00
struct FullscreenBlocker {
signal: Arc<AtomicBool>,
}
impl Blocker for FullscreenBlocker {
fn state(&self) -> BlockerState {
if self.signal.load(Ordering::SeqCst) {
BlockerState::Released
} else {
BlockerState::Pending
}
}
}
impl FullscreenSurface {
pub fn is_animating(&self) -> bool {
self.start_at.is_some() || self.ended_at.is_some()
}
2023-09-13 20:14:54 +02:00
}
impl IsAlive for FullscreenSurface {
fn alive(&self) -> bool {
2023-10-25 19:41:30 +02:00
self.surface.alive()
2023-09-13 20:14:54 +02:00
}
}
2022-09-28 12:01:29 +02:00
#[derive(Debug, Default)]
pub struct FocusStacks(HashMap<Seat<State>, IndexSet<CosmicMapped>>);
2023-10-25 19:41:30 +02:00
#[derive(Debug, Clone, PartialEq)]
pub struct ManagedState {
pub layer: ManagedLayer,
2023-10-25 19:41:30 +02:00
pub was_fullscreen: Option<FullscreenSurface>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ManagedLayer {
Tiling,
Floating,
Sticky,
}
#[derive(Debug, serde::Deserialize, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Left,
Right,
Up,
Down,
}
impl std::ops::Not for Direction {
type Output = Self;
fn not(self) -> Self::Output {
match self {
Direction::Left => Direction::Right,
Direction::Right => Direction::Left,
Direction::Up => Direction::Down,
Direction::Down => Direction::Up,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum FocusResult {
None,
Handled,
Some(KeyboardFocusTarget),
}
impl FocusResult {
pub fn or_else<F>(self, f: F) -> FocusResult
where
F: FnOnce() -> FocusResult,
{
match self {
FocusResult::None => f(),
x => x,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum MoveResult {
None,
Done,
MoveFurther(KeyboardFocusTarget),
ShiftFocus(KeyboardFocusTarget),
}
impl MoveResult {
pub fn or_else<F>(self, f: F) -> MoveResult
where
F: FnOnce() -> MoveResult,
{
match self {
MoveResult::None => f(),
x => x,
}
}
}
2022-03-24 20:32:31 +01:00
impl Workspace {
2023-10-25 19:40:26 +02:00
pub fn new(
handle: WorkspaceHandle,
output: Output,
tiling_enabled: bool,
theme: cosmic::Theme,
2023-10-25 19:40:26 +02:00
) -> Workspace {
let tiling_layer = TilingLayout::new(theme.clone(), &output);
let floating_layer = FloatingLayout::new(theme, &output);
let output_name = output.name();
2023-10-25 19:40:26 +02:00
2022-03-24 20:32:31 +01:00
Workspace {
2023-10-25 19:40:26 +02:00
output,
tiling_layer,
floating_layer,
2023-01-27 13:26:28 +01:00
tiling_enabled,
2023-10-25 19:41:30 +02:00
fullscreen: None,
handle,
2022-09-28 12:01:29 +02:00
focus_stack: FocusStacks::default(),
2022-11-03 18:51:27 +01:00
pending_buffers: Vec::new(),
screencopy_sessions: Vec::new(),
output_stack: {
let mut queue = VecDeque::new();
queue.push_back(output_name);
queue
},
2023-11-07 18:46:25 +01:00
pending_tokens: HashSet::new(),
2023-05-26 20:51:10 +02:00
backdrop_id: Id::new(),
dirty: AtomicBool::new(false),
2022-03-24 20:32:31 +01:00
}
}
2023-11-07 18:46:25 +01:00
pub fn refresh(&mut self, xdg_activation_state: &XdgActivationState) {
#[cfg(feature = "debug")]
puffin::profile_function!();
2023-10-25 19:41:30 +02:00
// TODO: `Option::take_if` once stabilitized
if self.fullscreen.as_ref().is_some_and(|w| !w.alive()) {
let _ = self.fullscreen.take();
};
2022-09-28 12:01:29 +02:00
self.floating_layer.refresh();
self.tiling_layer.refresh();
2023-11-07 18:46:25 +01:00
self.pending_tokens
.retain(|token| xdg_activation_state.data_for_token(token).is_some());
2022-09-28 12:01:29 +02:00
}
2023-09-11 21:11:34 +02:00
pub fn refresh_focus_stack(&mut self) {
let windows: Vec<CosmicMapped> = self.mapped().cloned().collect();
for stack in self.focus_stack.0.values_mut() {
stack.retain(|w| windows.contains(w));
}
}
2023-05-12 20:01:37 +02:00
pub fn animations_going(&self) -> bool {
self.tiling_layer.animations_going()
2023-11-14 16:32:49 +01:00
|| self.floating_layer.animations_going()
2023-09-15 18:37:34 +02:00
|| self
.fullscreen
2023-10-25 19:41:30 +02:00
.as_ref()
.is_some_and(|f| f.start_at.is_some() || f.ended_at.is_some())
|| self.dirty.swap(false, Ordering::SeqCst)
2023-05-12 20:01:37 +02:00
}
2023-07-05 23:48:10 +02:00
pub fn update_animations(&mut self) -> HashMap<ClientId, Client> {
let mut clients = HashMap::new();
2023-09-15 18:37:34 +02:00
2023-10-25 19:41:30 +02:00
if let Some(f) = self.fullscreen.as_mut() {
2023-09-15 18:37:34 +02:00
if let Some(start) = f.start_at.as_ref() {
let duration_since = Instant::now().duration_since(*start);
if duration_since > FULLSCREEN_ANIMATION_DURATION {
f.start_at.take();
self.dirty.store(true, Ordering::SeqCst);
2023-09-15 18:37:34 +02:00
}
if duration_since * 2 > FULLSCREEN_ANIMATION_DURATION {
if let Some(signal) = f.animation_signal.take() {
signal.store(true, Ordering::SeqCst);
if let Some(client) =
2023-10-25 19:41:30 +02:00
f.surface.wl_surface().as_ref().and_then(Resource::client)
2023-09-15 18:37:34 +02:00
{
clients.insert(client.id(), client);
2023-09-15 18:37:34 +02:00
}
}
}
}
2023-10-25 19:41:30 +02:00
if let Some(end) = f.ended_at {
let duration_since = Instant::now().duration_since(end);
2023-09-15 18:37:34 +02:00
if duration_since * 2 > FULLSCREEN_ANIMATION_DURATION {
if let Some(signal) = f.animation_signal.take() {
signal.store(true, Ordering::SeqCst);
if let Some(client) =
2023-10-25 19:41:30 +02:00
f.surface.wl_surface().as_ref().and_then(Resource::client)
2023-09-15 18:37:34 +02:00
{
clients.insert(client.id(), client);
2023-09-15 18:37:34 +02:00
}
}
}
2023-10-25 19:41:30 +02:00
if duration_since >= FULLSCREEN_ANIMATION_DURATION {
let _ = self.fullscreen.take();
self.dirty.store(true, Ordering::SeqCst);
}
2023-09-15 18:37:34 +02:00
}
}
2023-09-15 18:37:34 +02:00
clients.extend(self.tiling_layer.update_animation_state());
2023-11-14 16:32:49 +01:00
self.floating_layer.update_animation_state();
clients
2023-05-12 20:01:37 +02:00
}
2022-09-28 12:01:29 +02:00
pub fn commit(&mut self, surface: &WlSurface) {
2023-01-18 20:23:41 +01:00
if let Some(mapped) = self.element_for_wl_surface(surface) {
2022-09-28 12:01:29 +02:00
mapped
.windows()
.find(|(w, _)| w.wl_surface().as_ref() == Some(surface))
2022-09-28 12:01:29 +02:00
.unwrap()
.0
.on_commit();
}
}
2023-10-25 19:40:26 +02:00
pub fn output(&self) -> &Output {
&self.output
2022-09-28 12:01:29 +02:00
}
2023-10-25 19:40:26 +02:00
pub fn set_output(
&mut self,
output: &Output,
toplevel_info: &mut ToplevelInfoState<State, CosmicSurface>,
) {
2023-10-25 19:40:26 +02:00
self.tiling_layer.set_output(output);
self.floating_layer.set_output(output);
for mapped in self.mapped() {
for (surface, _) in mapped.windows() {
toplevel_info.toplevel_leave_output(&surface, &self.output);
toplevel_info.toplevel_enter_output(&surface, output);
}
2022-09-28 12:01:29 +02:00
}
let output_name = output.name();
if let Some(pos) = self
.output_stack
.iter()
.position(|name| name == &output_name)
{
self.output_stack.truncate(pos + 1);
} else {
self.output_stack.push_back(output.name());
}
2023-10-25 19:40:26 +02:00
self.output = output.clone();
2022-09-28 12:01:29 +02:00
}
pub fn preferrs_output(&self, output: &Output) -> bool {
self.output_stack.contains(&output.name())
}
pub fn unmap(&mut self, mapped: &CosmicMapped) -> Option<ManagedState> {
2023-10-25 19:41:30 +02:00
let was_fullscreen = self
.fullscreen
.as_ref()
.filter(|f| f.ended_at.is_none())
.map(|f| mapped.windows().any(|(w, _)| w == f.surface))
.unwrap_or(false)
.then(|| self.fullscreen.take().unwrap());
2023-10-12 12:05:43 +02:00
if mapped.maximized_state.lock().unwrap().is_some() {
2023-10-25 19:41:30 +02:00
// If surface is maximized then unmaximize it, so it is assigned to only one layer
let _ = self.unmaximize_request(mapped);
2023-10-25 19:41:30 +02:00
}
let was_floating = self.floating_layer.unmap(&mapped);
2023-10-25 19:40:26 +02:00
let was_tiling = self.tiling_layer.unmap(&mapped);
if was_floating || was_tiling {
assert!(was_floating != was_tiling);
}
self.focus_stack
.0
.values_mut()
.for_each(|set| set.retain(|m| m != mapped));
if was_floating {
Some(ManagedState {
layer: ManagedLayer::Floating,
2023-10-25 19:41:30 +02:00
was_fullscreen,
})
} else if was_tiling {
Some(ManagedState {
layer: ManagedLayer::Tiling,
2023-10-25 19:41:30 +02:00
was_fullscreen,
})
} else {
None
}
}
2023-01-18 20:23:41 +01:00
pub fn element_for_surface(&self, surface: &CosmicSurface) -> Option<&CosmicMapped> {
self.floating_layer
.mapped()
.chain(self.tiling_layer.mapped().map(|(_, w, _)| w))
.find(|e| e.windows().any(|(w, _)| &w == surface))
}
pub fn element_for_wl_surface(&self, surface: &WlSurface) -> Option<&CosmicMapped> {
2022-09-28 12:01:29 +02:00
self.floating_layer
.mapped()
.chain(self.tiling_layer.mapped().map(|(_, w, _)| w))
.find(|e| {
e.windows()
.any(|(w, _)| w.wl_surface().as_ref() == Some(surface))
2023-10-25 19:41:30 +02:00
})
2022-09-28 12:01:29 +02:00
}
pub fn element_under(
2023-07-21 16:08:55 +02:00
&mut self,
2023-10-25 19:40:26 +02:00
location: Point<f64, Global>,
2023-07-21 16:08:55 +02:00
overview: OverviewMode,
2023-10-25 19:40:26 +02:00
) -> Option<(PointerFocusTarget, Point<i32, Global>)> {
let location = location.to_local(&self.output);
2022-09-28 12:01:29 +02:00
self.floating_layer
.element_under(location)
2023-07-21 16:08:55 +02:00
.or_else(|| self.tiling_layer.element_under(location, overview))
2023-10-25 19:40:26 +02:00
.map(|(m, p)| (m, p.to_global(&self.output)))
2022-09-28 12:01:29 +02:00
}
2023-10-25 19:40:26 +02:00
pub fn element_geometry(&self, elem: &CosmicMapped) -> Option<Rectangle<i32, Local>> {
2022-09-28 12:01:29 +02:00
self.floating_layer
.element_geometry(elem)
.or_else(|| self.tiling_layer.element_geometry(elem))
}
2023-10-25 19:40:26 +02:00
pub fn recalculate(&mut self) {
self.tiling_layer.recalculate();
self.floating_layer.refresh();
2023-09-13 20:14:54 +02:00
}
pub fn unmaximize_request(&mut self, elem: &CosmicMapped) -> Option<Size<i32, Logical>> {
let mut state = elem.maximized_state.lock().unwrap();
if let Some(state) = state.take() {
match state.original_layer {
ManagedLayer::Tiling if self.tiling_enabled => {
// should still be mapped in tiling
self.floating_layer.unmap(&elem);
elem.output_enter(&self.output, elem.bbox());
elem.set_maximized(false);
elem.set_geometry(state.original_geometry.to_global(&self.output));
elem.configure();
self.tiling_layer.recalculate();
self.tiling_layer
.element_geometry(&elem)
.map(|geo| geo.size.as_logical())
}
ManagedLayer::Sticky => unreachable!(),
_ => {
elem.set_maximized(false);
self.floating_layer.map_internal(
elem.clone(),
Some(state.original_geometry.loc),
Some(state.original_geometry.size.as_logical()),
2024-01-05 20:38:13 +00:00
None,
);
Some(state.original_geometry.size.as_logical())
2023-10-25 19:41:30 +02:00
}
}
} else {
None
}
}
2022-03-24 20:32:31 +01:00
pub fn fullscreen_request(
&mut self,
window: &CosmicSurface,
previously: Option<(ManagedLayer, WorkspaceHandle)>,
) {
2023-10-24 15:58:53 +02:00
if self
.fullscreen
.as_ref()
.filter(|f| f.ended_at.is_none())
.is_some()
{
2022-04-22 15:18:28 +02:00
return;
}
window.set_fullscreen(true);
2023-10-25 19:41:30 +02:00
let geo = self.output.geometry();
let original_geometry = window.geometry().as_global();
2023-09-15 18:37:34 +02:00
let signal = if let Some(surface) = window.wl_surface() {
let signal = Arc::new(AtomicBool::new(false));
add_blocker(
&surface,
FullscreenBlocker {
signal: signal.clone(),
},
);
Some(signal)
} else {
None
};
window.set_geometry(geo);
2023-10-25 19:41:30 +02:00
window.send_configure();
self.fullscreen = Some(FullscreenSurface {
surface: window.clone(),
previously,
2023-10-25 19:41:30 +02:00
original_geometry,
start_at: Some(Instant::now()),
ended_at: None,
animation_signal: signal,
});
2022-04-22 15:18:28 +02:00
}
2022-05-03 13:37:51 +02:00
#[must_use]
pub fn unfullscreen_request(
&mut self,
window: &CosmicSurface,
) -> Option<(ManagedLayer, WorkspaceHandle)> {
2023-10-24 15:58:53 +02:00
if let Some(f) = self
.fullscreen
.as_mut()
.filter(|f| &f.surface == window && f.ended_at.is_none())
{
window.set_fullscreen(false);
2023-10-25 19:41:30 +02:00
window.set_geometry(f.original_geometry);
2023-01-03 19:17:51 +01:00
self.floating_layer.refresh();
2023-10-25 19:41:30 +02:00
self.tiling_layer.recalculate();
2023-01-03 19:17:51 +01:00
self.tiling_layer.refresh();
2023-10-25 19:41:30 +02:00
2023-09-15 18:37:34 +02:00
let signal = if let Some(surface) = window.wl_surface() {
let signal = Arc::new(AtomicBool::new(false));
add_blocker(
&surface,
FullscreenBlocker {
signal: signal.clone(),
},
);
Some(signal)
} else {
None
};
window.send_configure();
2023-09-15 18:37:34 +02:00
f.ended_at = Some(
Instant::now()
- (FULLSCREEN_ANIMATION_DURATION
- f.start_at
.take()
.map(|earlier| {
Instant::now()
.duration_since(earlier)
.min(FULLSCREEN_ANIMATION_DURATION)
})
.unwrap_or(FULLSCREEN_ANIMATION_DURATION)),
);
if let Some(new_signal) = signal {
if let Some(old_signal) = f.animation_signal.replace(new_signal) {
old_signal.store(true, Ordering::SeqCst);
}
}
f.previously
} else {
None
2023-09-13 20:14:54 +02:00
}
}
#[must_use]
pub fn remove_fullscreen(&mut self) -> Option<(CosmicMapped, ManagedLayer, WorkspaceHandle)> {
2023-10-25 19:41:30 +02:00
if let Some(surface) = self.fullscreen.as_ref().map(|f| f.surface.clone()) {
self.unfullscreen_request(&surface)
.map(|(l, h)| (self.element_for_surface(&surface).unwrap().clone(), l, h))
} else {
None
2022-04-22 15:18:28 +02:00
}
}
2023-10-25 19:41:30 +02:00
pub fn get_fullscreen(&self) -> Option<&CosmicSurface> {
2023-09-13 20:14:54 +02:00
self.fullscreen
2023-10-25 19:41:30 +02:00
.as_ref()
.filter(|f| f.alive())
.filter(|f| f.ended_at.is_none() && f.start_at.is_none())
2023-10-25 19:41:30 +02:00
.map(|f| &f.surface)
2022-04-22 15:18:28 +02:00
}
2022-07-07 22:41:17 +02:00
pub fn resize(
&mut self,
focused: &KeyboardFocusTarget,
direction: ResizeDirection,
edge: ResizeEdge,
amount: i32,
) -> bool {
if let Some(toplevel) = focused.toplevel() {
if self
.fullscreen
2023-10-25 19:41:30 +02:00
.as_ref()
.is_some_and(|f| f.surface.wl_surface().as_ref() == Some(&toplevel))
{
return false;
}
}
if !self.floating_layer.resize(focused, direction, edge, amount) {
self.tiling_layer.resize(focused, direction, edge, amount)
} else {
true
}
}
2024-01-03 17:11:50 +00:00
pub fn toggle_tiling(
&mut self,
seat: &Seat<State>,
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
) {
2024-01-05 18:33:16 +00:00
self.set_tiling(!self.tiling_enabled, seat, workspace_state)
}
pub fn set_tiling(
&mut self,
tiling: bool,
seat: &Seat<State>,
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
) {
if tiling {
2022-09-28 12:01:29 +02:00
let focus_stack = self.focus_stack.get(seat);
for window in self
.floating_layer
.mapped()
.cloned()
.collect::<Vec<_>>()
.into_iter()
{
self.floating_layer.unmap(&window);
self.tiling_layer
.map(window, Some(focus_stack.iter()), None)
2022-07-07 22:41:17 +02:00
}
2024-01-03 17:11:50 +00:00
workspace_state.set_workspace_tiling_state(&self.handle, TilingState::TilingEnabled);
2022-07-07 22:41:17 +02:00
self.tiling_enabled = true;
2024-01-05 18:33:16 +00:00
} else {
for window in self
.tiling_layer
.mapped()
.map(|(_, m, _)| m.clone())
.collect::<Vec<_>>()
.into_iter()
{
self.tiling_layer.unmap(&window);
self.floating_layer.map(window, None);
}
workspace_state.set_workspace_tiling_state(&self.handle, TilingState::FloatingOnly);
self.tiling_enabled = false;
2022-07-07 22:41:17 +02:00
}
}
pub fn toggle_floating_window(&mut self, seat: &Seat<State>, window: &CosmicMapped) {
2022-07-07 22:41:17 +02:00
if self.tiling_enabled {
if window.is_maximized(false) {
self.unmaximize_request(window);
}
if self.tiling_layer.mapped().any(|(_, m, _)| m == window) {
self.tiling_layer.unmap(window);
self.floating_layer.map(window.clone(), None);
} else if self.floating_layer.mapped().any(|w| w == window) {
let focus_stack = self.focus_stack.get(seat);
self.floating_layer.unmap(&window);
self.tiling_layer
.map(window.clone(), Some(focus_stack.iter()), None)
2022-07-07 22:41:17 +02:00
}
}
}
2022-09-28 12:01:29 +02:00
pub fn toggle_floating_window_focused(&mut self, seat: &Seat<State>) {
let maybe_window = self.focus_stack.get(seat).iter().next().cloned();
if let Some(window) = maybe_window {
self.toggle_floating_window(seat, &window);
}
}
2022-09-28 12:01:29 +02:00
pub fn mapped(&self) -> impl Iterator<Item = &CosmicMapped> {
self.floating_layer
.mapped()
.chain(self.tiling_layer.mapped().map(|(_, w, _)| w))
}
pub fn outputs(&self) -> impl Iterator<Item = &Output> {
self.floating_layer.space.outputs()
}
pub fn windows(&self) -> impl Iterator<Item = CosmicSurface> + '_ {
2022-09-28 12:01:29 +02:00
self.floating_layer
.windows()
.chain(self.tiling_layer.windows().map(|(_, w, _)| w))
}
pub fn is_fullscreen(&self, mapped: &CosmicMapped) -> bool {
self.fullscreen
2023-10-25 19:41:30 +02:00
.as_ref()
.is_some_and(|f| f.surface == mapped.active_window())
}
2023-01-18 20:23:41 +01:00
pub fn is_floating(&self, mapped: &CosmicMapped) -> bool {
2023-10-25 19:41:30 +02:00
!self.is_fullscreen(mapped) && self.floating_layer.mapped().any(|m| m == mapped)
}
pub fn is_tiled(&self, mapped: &CosmicMapped) -> bool {
2023-10-25 19:41:30 +02:00
!self.is_fullscreen(mapped) && self.tiling_layer.mapped().any(|(_, m, _)| m == mapped)
2023-01-18 20:23:41 +01:00
}
pub fn node_desc(&self, focus: KeyboardFocusTarget) -> Option<NodeDesc> {
match focus {
KeyboardFocusTarget::Element(mapped) => {
2023-10-25 19:40:26 +02:00
self.tiling_layer.mapped().find_map(|(_, m, _)| {
if m == &mapped {
mapped
.tiling_node_id
.lock()
.unwrap()
.clone()
.map(|node_id| NodeDesc {
handle: self.handle.clone(),
node: node_id,
stack_window: if mapped
.stack_ref()
.map(|stack| !stack.whole_stack_focused())
.unwrap_or(false)
{
Some(mapped.active_window())
} else {
None
},
})
2023-10-25 19:40:26 +02:00
} else {
None
}
})
}
2023-10-25 19:40:26 +02:00
KeyboardFocusTarget::Group(WindowGroup { node, .. }) => Some(NodeDesc {
handle: self.handle.clone(),
node,
stack_window: None,
}),
_ => None,
}
}
2023-10-25 19:40:26 +02:00
pub fn render<'a, R>(
2022-09-28 12:01:29 +02:00
&self,
renderer: &mut R,
2023-02-25 00:17:54 +01:00
draw_focus_indicator: Option<&Seat<State>>,
overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree<Data>>)>),
2023-07-06 00:03:26 +02:00
resize_indicator: Option<(ResizeMode, ResizeIndicator)>,
indicator_thickness: u8,
theme: &CosmicTheme,
) -> Result<
(
Vec<WorkspaceRenderElement<R>>,
Vec<WorkspaceRenderElement<R>>,
),
OutputNotMapped,
>
2022-09-28 12:01:29 +02:00
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
2022-09-28 12:01:29 +02:00
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
2023-03-07 22:20:44 +01:00
CosmicWindowRenderElement<R>: RenderElement<R>,
2023-06-08 13:19:30 +02:00
CosmicStackRenderElement<R>: RenderElement<R>,
WorkspaceRenderElement<R>: RenderElement<R>,
2022-09-28 12:01:29 +02:00
{
#[cfg(feature = "debug")]
puffin::profile_function!();
let mut window_elements = Vec::new();
let mut popup_elements = Vec::new();
2022-09-28 12:01:29 +02:00
let output_scale = self.output.current_scale().fractional_scale();
let zone = {
let layer_map = layer_map_for_output(&self.output);
layer_map.non_exclusive_zone().as_local()
};
2022-09-28 12:01:29 +02:00
2023-10-25 19:41:30 +02:00
if let Some(fullscreen) = self.fullscreen.as_ref() {
2022-09-28 12:01:29 +02:00
// fullscreen window
2023-10-25 19:41:30 +02:00
let bbox = fullscreen.surface.bbox().as_local();
2023-09-15 18:37:34 +02:00
let element_geo = Rectangle::from_loc_and_size(
2023-10-25 19:41:30 +02:00
self.element_for_surface(&fullscreen.surface)
2023-09-15 18:37:34 +02:00
.and_then(|elem| {
self.floating_layer
.element_geometry(elem)
.or_else(|| self.tiling_layer.element_geometry(elem))
.map(|mut geo| {
2023-10-25 19:41:30 +02:00
geo.loc -= elem.geometry().loc.as_local();
2023-09-15 18:37:34 +02:00
geo
})
})
.unwrap_or(bbox)
.loc,
2023-10-25 19:41:30 +02:00
fullscreen.original_geometry.size.as_local(),
2023-09-15 18:37:34 +02:00
);
2023-10-25 19:41:30 +02:00
let mut full_geo =
Rectangle::from_loc_and_size((0, 0), self.output.geometry().size.as_local());
2023-09-15 18:37:34 +02:00
if fullscreen.start_at.is_none() {
if bbox != full_geo {
if bbox.size.w < full_geo.size.w {
full_geo.loc.x += (full_geo.size.w - bbox.size.w) / 2;
full_geo.size.w = bbox.size.w;
}
if bbox.size.h < full_geo.size.h {
full_geo.loc.y += (full_geo.size.h - bbox.size.h) / 2;
full_geo.size.h = bbox.size.h;
}
}
}
2023-09-15 18:37:34 +02:00
let (target_geo, alpha) = match (fullscreen.start_at, fullscreen.ended_at) {
(Some(started), _) => {
let duration = Instant::now().duration_since(started).as_secs_f64()
/ FULLSCREEN_ANIMATION_DURATION.as_secs_f64();
(
ease(
EaseInOutCubic,
EaseRectangle(element_geo),
EaseRectangle(full_geo),
duration,
)
.0,
ease(EaseInOutCubic, 0.0, 1.0, duration),
)
}
(_, Some(ended)) => {
let duration = Instant::now().duration_since(ended).as_secs_f64()
/ FULLSCREEN_ANIMATION_DURATION.as_secs_f64();
(
ease(
EaseInOutCubic,
EaseRectangle(full_geo),
EaseRectangle(element_geo),
duration,
)
.0,
ease(EaseInOutCubic, 1.0, 0.0, duration),
)
}
(None, None) => (full_geo, 1.0),
};
let render_loc = target_geo
.loc
.as_logical()
.to_physical_precise_round(output_scale);
2023-09-15 18:37:34 +02:00
let scale = Scale {
x: target_geo.size.w as f64 / bbox.size.w as f64,
y: target_geo.size.h as f64 / bbox.size.h as f64,
};
let (w_elements, p_elements) = fullscreen
2023-10-25 19:41:30 +02:00
.surface
.split_render_elements::<R, CosmicWindowRenderElement<R>>(
2023-09-15 18:37:34 +02:00
renderer,
render_loc,
output_scale.into(),
alpha,
);
window_elements.extend(
w_elements
.into_iter()
.map(|elem| RescaleRenderElement::from_element(elem, render_loc, scale))
.map(Into::into),
2023-09-15 18:37:34 +02:00
);
popup_elements.extend(p_elements.into_iter().map(Into::into));
2023-09-15 18:37:34 +02:00
}
if self
.fullscreen
2023-10-25 19:41:30 +02:00
.as_ref()
2023-09-15 18:37:34 +02:00
.map(|f| f.start_at.is_some() || f.ended_at.is_some())
.unwrap_or(true)
{
let focused = draw_focus_indicator
2023-10-25 19:41:30 +02:00
.filter(|_| !self.fullscreen.is_some())
.and_then(|seat| self.focus_stack.get(seat).last().cloned());
2022-09-28 12:01:29 +02:00
// floating surfaces
let alpha = match &overview.0 {
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,
};
2023-10-25 19:40:26 +02:00
let (w_elements, p_elements) = self.floating_layer.render::<R>(
renderer,
focused.as_ref(),
resize_indicator.clone(),
indicator_thickness,
alpha,
theme,
2022-09-28 12:01:29 +02:00
);
popup_elements.extend(p_elements.into_iter().map(WorkspaceRenderElement::from));
window_elements.extend(w_elements.into_iter().map(WorkspaceRenderElement::from));
2022-09-28 12:01:29 +02:00
let alpha = match &overview.0 {
OverviewMode::Started(_, start) => Some(
(Instant::now().duration_since(*start).as_millis() as f64 / 100.0).min(1.0)
as f32,
),
OverviewMode::Ended(_, ended) => Some(
1.0 - (Instant::now().duration_since(*ended).as_millis() as f64 / 100.0)
.min(1.0) as f32,
),
_ => None,
};
2022-09-28 12:01:29 +02:00
//tiling surfaces
2023-10-25 19:40:26 +02:00
let (w_elements, p_elements) = self.tiling_layer.render::<R>(
renderer,
draw_focus_indicator,
2023-10-25 19:40:26 +02:00
zone,
overview,
resize_indicator,
indicator_thickness,
theme,
)?;
popup_elements.extend(p_elements.into_iter().map(WorkspaceRenderElement::from));
window_elements.extend(w_elements.into_iter().map(WorkspaceRenderElement::from));
2022-09-28 12:01:29 +02:00
2023-05-26 20:51:10 +02:00
if let Some(alpha) = alpha {
window_elements.push(
2023-05-26 20:51:10 +02:00
Into::<CosmicMappedRenderElement<R>>::into(BackdropShader::element(
renderer,
self.backdrop_id.clone(),
zone,
0.,
2023-06-01 17:03:55 +02:00
alpha * 0.85,
2023-05-26 20:51:10 +02:00
[0.0, 0.0, 0.0],
))
.into(),
)
}
2022-09-28 12:01:29 +02:00
}
Ok((window_elements, popup_elements))
2022-09-28 12:01:29 +02:00
}
}
impl FocusStacks {
pub fn get<'a>(&'a self, seat: &Seat<State>) -> FocusStack<'a> {
FocusStack(self.0.get(seat))
}
pub fn get_mut<'a>(&'a mut self, seat: &Seat<State>) -> FocusStackMut<'a> {
FocusStackMut(self.0.entry(seat.clone()).or_default())
}
}
pub struct OutputNotMapped;
pub enum WorkspaceRenderElement<R>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
{
OverrideRedirect(WaylandSurfaceRenderElement<R>),
Fullscreen(RescaleRenderElement<CosmicWindowRenderElement<R>>),
FullscreenPopup(CosmicWindowRenderElement<R>),
Window(CosmicMappedRenderElement<R>),
Backdrop(TextureRenderElement<GlesTexture>),
}
impl<R> Element for WorkspaceRenderElement<R>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
{
fn id(&self) -> &smithay::backend::renderer::element::Id {
match self {
WorkspaceRenderElement::OverrideRedirect(elem) => elem.id(),
2023-09-15 18:37:34 +02:00
WorkspaceRenderElement::Fullscreen(elem) => elem.id(),
WorkspaceRenderElement::FullscreenPopup(elem) => elem.id(),
WorkspaceRenderElement::Window(elem) => elem.id(),
WorkspaceRenderElement::Backdrop(elem) => elem.id(),
}
}
fn current_commit(&self) -> smithay::backend::renderer::utils::CommitCounter {
match self {
WorkspaceRenderElement::OverrideRedirect(elem) => elem.current_commit(),
2023-09-15 18:37:34 +02:00
WorkspaceRenderElement::Fullscreen(elem) => elem.current_commit(),
WorkspaceRenderElement::FullscreenPopup(elem) => elem.current_commit(),
WorkspaceRenderElement::Window(elem) => elem.current_commit(),
WorkspaceRenderElement::Backdrop(elem) => elem.current_commit(),
}
}
fn src(&self) -> Rectangle<f64, smithay::utils::Buffer> {
match self {
WorkspaceRenderElement::OverrideRedirect(elem) => elem.src(),
2023-09-15 18:37:34 +02:00
WorkspaceRenderElement::Fullscreen(elem) => elem.src(),
WorkspaceRenderElement::FullscreenPopup(elem) => elem.src(),
WorkspaceRenderElement::Window(elem) => elem.src(),
WorkspaceRenderElement::Backdrop(elem) => elem.src(),
}
}
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, smithay::utils::Physical> {
match self {
WorkspaceRenderElement::OverrideRedirect(elem) => elem.geometry(scale),
2023-09-15 18:37:34 +02:00
WorkspaceRenderElement::Fullscreen(elem) => elem.geometry(scale),
WorkspaceRenderElement::FullscreenPopup(elem) => elem.geometry(scale),
WorkspaceRenderElement::Window(elem) => elem.geometry(scale),
WorkspaceRenderElement::Backdrop(elem) => elem.geometry(scale),
}
}
fn location(&self, scale: Scale<f64>) -> Point<i32, smithay::utils::Physical> {
match self {
WorkspaceRenderElement::OverrideRedirect(elem) => elem.location(scale),
2023-09-15 18:37:34 +02:00
WorkspaceRenderElement::Fullscreen(elem) => elem.location(scale),
WorkspaceRenderElement::FullscreenPopup(elem) => elem.location(scale),
WorkspaceRenderElement::Window(elem) => elem.location(scale),
WorkspaceRenderElement::Backdrop(elem) => elem.location(scale),
}
}
fn transform(&self) -> smithay::utils::Transform {
match self {
WorkspaceRenderElement::OverrideRedirect(elem) => elem.transform(),
2023-09-15 18:37:34 +02:00
WorkspaceRenderElement::Fullscreen(elem) => elem.transform(),
WorkspaceRenderElement::FullscreenPopup(elem) => elem.transform(),
WorkspaceRenderElement::Window(elem) => elem.transform(),
WorkspaceRenderElement::Backdrop(elem) => elem.transform(),
}
}
fn damage_since(
&self,
scale: Scale<f64>,
commit: Option<smithay::backend::renderer::utils::CommitCounter>,
) -> Vec<Rectangle<i32, smithay::utils::Physical>> {
match self {
WorkspaceRenderElement::OverrideRedirect(elem) => elem.damage_since(scale, commit),
2023-09-15 18:37:34 +02:00
WorkspaceRenderElement::Fullscreen(elem) => elem.damage_since(scale, commit),
WorkspaceRenderElement::FullscreenPopup(elem) => elem.damage_since(scale, commit),
WorkspaceRenderElement::Window(elem) => elem.damage_since(scale, commit),
WorkspaceRenderElement::Backdrop(elem) => elem.damage_since(scale, commit),
}
}
fn opaque_regions(&self, scale: Scale<f64>) -> Vec<Rectangle<i32, smithay::utils::Physical>> {
match self {
WorkspaceRenderElement::OverrideRedirect(elem) => elem.opaque_regions(scale),
2023-09-15 18:37:34 +02:00
WorkspaceRenderElement::Fullscreen(elem) => elem.opaque_regions(scale),
WorkspaceRenderElement::FullscreenPopup(elem) => elem.opaque_regions(scale),
WorkspaceRenderElement::Window(elem) => elem.opaque_regions(scale),
WorkspaceRenderElement::Backdrop(elem) => elem.opaque_regions(scale),
}
}
2023-05-12 20:01:37 +02:00
fn alpha(&self) -> f32 {
match self {
WorkspaceRenderElement::OverrideRedirect(elem) => elem.alpha(),
2023-09-15 18:37:34 +02:00
WorkspaceRenderElement::Fullscreen(elem) => elem.alpha(),
WorkspaceRenderElement::FullscreenPopup(elem) => elem.alpha(),
2023-05-12 20:01:37 +02:00
WorkspaceRenderElement::Window(elem) => elem.alpha(),
WorkspaceRenderElement::Backdrop(elem) => elem.alpha(),
2023-05-12 20:01:37 +02:00
}
}
}
impl RenderElement<GlowRenderer> for WorkspaceRenderElement<GlowRenderer> {
fn draw<'frame>(
&self,
frame: &mut GlowFrame<'frame>,
src: Rectangle<f64, BufferCoords>,
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, smithay::utils::Physical>],
) -> Result<(), GlesError> {
match self {
WorkspaceRenderElement::OverrideRedirect(elem) => elem.draw(frame, src, dst, damage),
2023-09-15 18:37:34 +02:00
WorkspaceRenderElement::Fullscreen(elem) => elem.draw(frame, src, dst, damage),
WorkspaceRenderElement::FullscreenPopup(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(
&self,
renderer: &mut GlowRenderer,
) -> Option<smithay::backend::renderer::element::UnderlyingStorage> {
match self {
WorkspaceRenderElement::OverrideRedirect(elem) => elem.underlying_storage(renderer),
2023-09-15 18:37:34 +02:00
WorkspaceRenderElement::Fullscreen(elem) => elem.underlying_storage(renderer),
WorkspaceRenderElement::FullscreenPopup(elem) => elem.underlying_storage(renderer),
WorkspaceRenderElement::Window(elem) => elem.underlying_storage(renderer),
WorkspaceRenderElement::Backdrop(elem) => elem.underlying_storage(renderer),
}
}
}
2024-02-07 12:33:32 +01:00
impl<'a> RenderElement<GlMultiRenderer<'a>> for WorkspaceRenderElement<GlMultiRenderer<'a>> {
fn draw<'frame>(
&self,
2024-02-07 12:33:32 +01:00
frame: &mut GlMultiFrame<'a, 'frame>,
2022-12-27 18:27:29 +01:00
src: Rectangle<f64, BufferCoords>,
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, smithay::utils::Physical>],
) -> Result<(), GlMultiError> {
match self {
WorkspaceRenderElement::OverrideRedirect(elem) => elem.draw(frame, src, dst, damage),
2023-09-15 18:37:34 +02:00
WorkspaceRenderElement::Fullscreen(elem) => elem.draw(frame, src, dst, damage),
WorkspaceRenderElement::FullscreenPopup(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,
2024-02-07 12:33:32 +01:00
renderer: &mut GlMultiRenderer<'a>,
2023-02-24 14:07:40 +01:00
) -> Option<smithay::backend::renderer::element::UnderlyingStorage> {
match self {
WorkspaceRenderElement::OverrideRedirect(elem) => elem.underlying_storage(renderer),
2023-09-15 18:37:34 +02:00
WorkspaceRenderElement::Fullscreen(elem) => elem.underlying_storage(renderer),
WorkspaceRenderElement::FullscreenPopup(elem) => elem.underlying_storage(renderer),
WorkspaceRenderElement::Window(elem) => elem.underlying_storage(renderer),
WorkspaceRenderElement::Backdrop(elem) => {
elem.underlying_storage(renderer.glow_renderer_mut())
}
}
}
}
impl<R> From<RescaleRenderElement<CosmicWindowRenderElement<R>>> for WorkspaceRenderElement<R>
2023-09-15 18:37:34 +02:00
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
{
fn from(elem: RescaleRenderElement<CosmicWindowRenderElement<R>>) -> Self {
2023-09-15 18:37:34 +02:00
WorkspaceRenderElement::Fullscreen(elem)
}
}
impl<R> From<CosmicWindowRenderElement<R>> for WorkspaceRenderElement<R>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
{
fn from(elem: CosmicWindowRenderElement<R>) -> Self {
WorkspaceRenderElement::FullscreenPopup(elem)
}
}
impl<R> From<WaylandSurfaceRenderElement<R>> for WorkspaceRenderElement<R>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
{
fn from(elem: WaylandSurfaceRenderElement<R>) -> Self {
WorkspaceRenderElement::OverrideRedirect(elem)
}
}
impl<R> From<CosmicMappedRenderElement<R>> for WorkspaceRenderElement<R>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
{
fn from(elem: CosmicMappedRenderElement<R>) -> Self {
WorkspaceRenderElement::Window(elem)
}
2022-03-24 20:32:31 +01:00
}
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)
}
}