2025-06-25 17:54:27 +02:00
|
|
|
use crate::shell::focus::FocusTarget;
|
|
|
|
|
use crate::shell::layout::tiling::RestoreTilingState;
|
2025-03-13 12:50:02 -07:00
|
|
|
use crate::wayland::handlers::xdg_activation::ActivationContext;
|
2022-07-04 15:28:03 +02:00
|
|
|
use crate::{
|
2023-05-19 19:44:57 +02:00
|
|
|
backend::render::{
|
2024-08-04 18:04:06 -07:00
|
|
|
element::{AsGlowRenderer, FromGlesError},
|
2024-09-27 23:41:58 +02:00
|
|
|
BackdropShader,
|
2023-05-19 19:44:57 +02:00
|
|
|
},
|
|
|
|
|
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
|
|
|
},
|
2022-07-04 15:28:03 +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::{
|
2024-03-12 19:42:48 +01:00
|
|
|
handlers::screencopy::ScreencopySessions,
|
2022-11-03 18:51:27 +01:00
|
|
|
protocols::{
|
2024-04-10 15:49:08 +02:00
|
|
|
toplevel_info::{toplevel_enter_output, toplevel_leave_output},
|
2024-01-03 17:11:50 +00:00
|
|
|
workspace::{WorkspaceHandle, WorkspaceUpdateGuard},
|
2022-11-03 18:51:27 +01:00
|
|
|
},
|
|
|
|
|
},
|
2022-07-04 15:28:03 +02:00
|
|
|
};
|
2025-01-31 14:13:33 -08:00
|
|
|
use cosmic_comp_config::workspace::{OutputMatch, PinnedWorkspace};
|
2022-07-04 15:28:03 +02:00
|
|
|
|
2023-10-10 13:55:34 -04:00
|
|
|
use cosmic::theme::CosmicTheme;
|
2025-02-19 14:07:51 -08:00
|
|
|
use cosmic_protocols::workspace::v2::server::zcosmic_workspace_handle_v2::TilingState;
|
2023-09-08 22:16:39 +02:00
|
|
|
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};
|
2025-06-25 17:54:27 +02:00
|
|
|
use smithay::output::WeakOutput;
|
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,
|
2023-12-20 21:15:53 +00:00
|
|
|
utils::RescaleRenderElement, Element, Id, RenderElement,
|
2023-05-19 19:44:57 +02:00
|
|
|
},
|
2024-08-04 18:04:06 -07:00
|
|
|
gles::GlesTexture,
|
|
|
|
|
glow::GlowRenderer,
|
2024-05-13 14:16:21 -07:00
|
|
|
utils::{DamageSet, OpaqueRegions},
|
2023-05-26 20:51:10 +02:00
|
|
|
ImportAll, ImportMem, Renderer,
|
2022-09-28 12:01:29 +02:00
|
|
|
},
|
2024-10-10 23:18:04 +02:00
|
|
|
desktop::{layer_map_for_output, space::SpaceElement, WindowSurfaceType},
|
2023-12-20 20:29:44 +00:00
|
|
|
input::Seat,
|
2022-11-03 18:51:27 +01:00
|
|
|
output::Output,
|
2025-06-25 17:54:27 +02:00
|
|
|
reexports::wayland_server::Client,
|
2023-05-22 16:55:29 +02:00
|
|
|
utils::{Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Size},
|
2025-06-25 17:54:27 +02:00
|
|
|
wayland::xdg_activation::XdgActivationState,
|
2022-09-28 12:01:29 +02:00
|
|
|
};
|
2023-09-15 18:37:34 +02:00
|
|
|
use std::{
|
2025-03-13 12:50:02 -07:00
|
|
|
collections::{HashMap, VecDeque},
|
2025-06-25 17:54:27 +02:00
|
|
|
sync::atomic::{AtomicBool, Ordering},
|
2023-09-15 18:37:34 +02:00
|
|
|
time::{Duration, Instant},
|
|
|
|
|
};
|
2023-07-06 00:02:29 +02:00
|
|
|
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,
|
2023-08-11 18:15:22 +02:00
|
|
|
swap_indicator::SwapIndicator, window::CosmicWindowRenderElement, CosmicMapped,
|
2024-03-07 12:22:39 +01:00
|
|
|
MaximizedState,
|
2023-07-06 00:03:26 +02:00
|
|
|
},
|
2023-07-11 16:33:23 +02:00
|
|
|
focus::{
|
2023-08-11 18:15:22 +02:00
|
|
|
target::{KeyboardFocusTarget, PointerFocusTarget, WindowGroup},
|
2023-12-22 15:48:35 +00:00
|
|
|
FocusStack, FocusStackMut,
|
2023-07-11 16:33:23 +02:00
|
|
|
},
|
2023-12-20 20:49:37 +00:00
|
|
|
grabs::ResizeEdge,
|
2025-06-25 17:54:27 +02:00
|
|
|
layout::tiling::{Data, NodeDesc},
|
2023-07-06 00:02:29 +02:00
|
|
|
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);
|
|
|
|
|
|
2025-01-31 14:13:33 -08:00
|
|
|
// For stable workspace id, generate random 24-bit integer, as a hex string
|
|
|
|
|
// Must be compared with existing workspaces work uniqueness.
|
|
|
|
|
// TODO: Assign an id to any workspace that is pinned
|
|
|
|
|
pub fn random_id() -> String {
|
|
|
|
|
let id = rand::random_range(0..(2 << 24));
|
|
|
|
|
format!("{:x}", id)
|
2025-03-26 12:54:46 -07:00
|
|
|
}
|
|
|
|
|
|
2025-01-31 14:13:33 -08:00
|
|
|
fn output_match_for_output(output: &Output) -> OutputMatch {
|
|
|
|
|
OutputMatch {
|
|
|
|
|
name: output.name(),
|
|
|
|
|
edid: output.edid().cloned(),
|
2025-03-26 12:54:46 -07:00
|
|
|
}
|
2025-01-31 14:13:33 -08:00
|
|
|
}
|
2025-03-26 12:54:46 -07:00
|
|
|
|
2025-01-31 14:13:33 -08:00
|
|
|
// If `disambguate` is true, check that edid *and* connector name match.
|
|
|
|
|
// Otherwise, match only edid (if it exists)
|
|
|
|
|
fn output_matches(output_match: &OutputMatch, output: &Output, disambiguate: bool) -> bool {
|
|
|
|
|
if output_match.edid.as_ref() != output.edid() {
|
|
|
|
|
false
|
|
|
|
|
} else if disambiguate || output_match.edid.is_none() {
|
|
|
|
|
output_match.name == output.name()
|
|
|
|
|
} else {
|
|
|
|
|
true
|
2025-03-26 12:54:46 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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,
|
2022-07-04 15:28:03 +02:00
|
|
|
pub tiling_layer: TilingLayout,
|
|
|
|
|
pub floating_layer: FloatingLayout,
|
2024-02-08 20:28:59 +01:00
|
|
|
pub minimized_windows: Vec<MinimizedWindow>,
|
2022-09-28 12:01:29 +02:00
|
|
|
pub tiling_enabled: bool,
|
2023-10-25 19:41:30 +02:00
|
|
|
pub fullscreen: Option<FullscreenSurface>,
|
2025-01-31 14:13:33 -08:00
|
|
|
pub pinned: bool,
|
2023-10-25 19:41:30 +02:00
|
|
|
|
2022-07-04 15:28:03 +02:00
|
|
|
pub handle: WorkspaceHandle,
|
2022-09-28 12:01:29 +02:00
|
|
|
pub focus_stack: FocusStacks,
|
2024-03-12 19:42:48 +01:00
|
|
|
pub screencopy: ScreencopySessions,
|
2025-03-26 12:54:46 -07:00
|
|
|
output_stack: VecDeque<OutputMatch>,
|
2023-05-26 20:51:10 +02:00
|
|
|
pub(super) backdrop_id: Id,
|
2023-09-20 14:56:18 +02:00
|
|
|
pub dirty: AtomicBool,
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
|
2024-02-08 20:28:59 +01:00
|
|
|
#[derive(Debug)]
|
2025-06-25 17:54:27 +02:00
|
|
|
pub enum MinimizedWindow {
|
|
|
|
|
Fullscreen {
|
|
|
|
|
surface: CosmicSurface,
|
|
|
|
|
previous: Option<FullscreenRestoreData>,
|
2024-02-23 17:25:40 +01:00
|
|
|
},
|
|
|
|
|
Floating {
|
2025-06-25 17:54:27 +02:00
|
|
|
window: CosmicMapped,
|
|
|
|
|
previous: FloatingRestoreData,
|
2024-02-23 17:25:40 +01:00
|
|
|
},
|
|
|
|
|
Tiling {
|
2025-06-25 17:54:27 +02:00
|
|
|
window: CosmicMapped,
|
|
|
|
|
previous: TilingRestoreData,
|
2024-02-23 17:25:40 +01:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
impl PartialEq<CosmicMapped> for MinimizedWindow {
|
|
|
|
|
fn eq(&self, other: &CosmicMapped) -> bool {
|
|
|
|
|
self.mapped().is_some_and(|m| m == other)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-23 17:25:40 +01:00
|
|
|
impl MinimizedWindow {
|
2025-06-25 17:54:27 +02:00
|
|
|
pub fn mapped(&self) -> Option<&CosmicMapped> {
|
|
|
|
|
match self {
|
|
|
|
|
MinimizedWindow::Floating { window, .. } | MinimizedWindow::Tiling { window, .. } => {
|
|
|
|
|
Some(window)
|
|
|
|
|
}
|
|
|
|
|
_ => None,
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-23 17:25:40 +01:00
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
pub fn mapped_mut(&mut self) -> Option<&mut CosmicMapped> {
|
|
|
|
|
match self {
|
|
|
|
|
MinimizedWindow::Floating { window, .. } | MinimizedWindow::Tiling { window, .. } => {
|
|
|
|
|
Some(window)
|
2024-02-23 17:25:40 +01:00
|
|
|
}
|
2025-06-25 17:54:27 +02:00
|
|
|
_ => None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn active_window(&self) -> CosmicSurface {
|
|
|
|
|
match self {
|
|
|
|
|
MinimizedWindow::Floating { window, .. } | MinimizedWindow::Tiling { window, .. } => {
|
|
|
|
|
window.active_window()
|
2024-02-23 17:25:40 +01:00
|
|
|
}
|
2025-06-25 17:54:27 +02:00
|
|
|
MinimizedWindow::Fullscreen { surface, .. } => surface.clone(),
|
2024-02-23 17:25:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
pub fn windows(&self) -> impl Iterator<Item = CosmicSurface> + '_ {
|
|
|
|
|
match self {
|
|
|
|
|
MinimizedWindow::Floating { window, .. } | MinimizedWindow::Tiling { window, .. } => {
|
|
|
|
|
Box::new(window.windows().map(|(s, _)| s))
|
|
|
|
|
as Box<dyn Iterator<Item = CosmicSurface>>
|
|
|
|
|
}
|
|
|
|
|
MinimizedWindow::Fullscreen { surface, .. } => {
|
|
|
|
|
Box::new(std::iter::once(surface.clone())) as _
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn unmaximize(&mut self, original_geometry: Rectangle<i32, Local>) {
|
|
|
|
|
match self {
|
|
|
|
|
MinimizedWindow::Fullscreen { .. } => {}
|
|
|
|
|
MinimizedWindow::Tiling {
|
|
|
|
|
window, previous, ..
|
|
|
|
|
} => {
|
|
|
|
|
previous.was_maximized = false;
|
|
|
|
|
window.set_maximized(false);
|
|
|
|
|
window.configure();
|
|
|
|
|
}
|
|
|
|
|
MinimizedWindow::Floating {
|
|
|
|
|
window, previous, ..
|
|
|
|
|
} => {
|
|
|
|
|
previous.geometry = original_geometry;
|
|
|
|
|
window.set_maximized(false);
|
|
|
|
|
window.configure();
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-23 17:25:40 +01:00
|
|
|
}
|
2024-02-08 20:28:59 +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,
|
2025-06-25 17:54:27 +02:00
|
|
|
pub previous_state: Option<FullscreenRestoreState>,
|
|
|
|
|
pub previous_geometry: Option<Rectangle<i32, Local>>,
|
2023-09-15 18:37:34 +02:00
|
|
|
start_at: Option<Instant>,
|
2025-06-25 17:54:27 +02:00
|
|
|
pub ended_at: Option<Instant>,
|
2023-09-15 18:37:34 +02:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-04 11:13:59 -05:00
|
|
|
/// LIFO stack of focus targets
|
2022-09-28 12:01:29 +02:00
|
|
|
#[derive(Debug, Default)]
|
2025-06-25 17:54:27 +02:00
|
|
|
pub struct FocusStacks(HashMap<Seat<State>, IndexSet<FocusTarget>>);
|
2022-09-28 12:01:29 +02:00
|
|
|
|
2023-09-20 18:57:58 +02:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
|
pub enum ManagedLayer {
|
2025-06-25 17:54:27 +02:00
|
|
|
Fullscreen,
|
2022-11-14 11:54:22 +01:00
|
|
|
Tiling,
|
|
|
|
|
Floating,
|
2023-12-20 20:29:44 +00:00
|
|
|
Sticky,
|
2022-11-14 11:54:22 +01:00
|
|
|
}
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub enum FullscreenRestoreState {
|
|
|
|
|
Tiling {
|
|
|
|
|
workspace: WorkspaceHandle,
|
|
|
|
|
state: TilingRestoreData,
|
|
|
|
|
},
|
|
|
|
|
Floating {
|
|
|
|
|
workspace: WorkspaceHandle,
|
|
|
|
|
state: FloatingRestoreData,
|
|
|
|
|
},
|
|
|
|
|
Sticky {
|
|
|
|
|
output: WeakOutput,
|
|
|
|
|
state: FloatingRestoreData,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub enum WorkspaceRestoreData {
|
|
|
|
|
Fullscreen(Option<FullscreenRestoreData>),
|
|
|
|
|
Tiling(Option<TilingRestoreData>),
|
|
|
|
|
Floating(Option<FloatingRestoreData>),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<ManagedLayer> for WorkspaceRestoreData {
|
|
|
|
|
fn from(value: ManagedLayer) -> Self {
|
|
|
|
|
match value {
|
|
|
|
|
ManagedLayer::Floating | ManagedLayer::Sticky => WorkspaceRestoreData::Floating(None),
|
|
|
|
|
ManagedLayer::Tiling => WorkspaceRestoreData::Tiling(None),
|
|
|
|
|
ManagedLayer::Fullscreen => WorkspaceRestoreData::Fullscreen(None),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct FloatingRestoreData {
|
|
|
|
|
pub geometry: Rectangle<i32, Local>,
|
|
|
|
|
pub output_size: Size<i32, Logical>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FloatingRestoreData {
|
|
|
|
|
pub fn position_relative(&self, output_size: Size<i32, Logical>) -> Point<i32, Local> {
|
|
|
|
|
if self.output_size != output_size {
|
|
|
|
|
Point::from((
|
|
|
|
|
(self.geometry.loc.x as f64 / self.output_size.w as f64 * output_size.w as f64)
|
|
|
|
|
.floor() as i32,
|
|
|
|
|
(self.geometry.loc.y as f64 / self.output_size.h as f64 * output_size.h as f64)
|
|
|
|
|
.floor() as i32,
|
|
|
|
|
))
|
|
|
|
|
} else {
|
|
|
|
|
self.geometry.loc
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct TilingRestoreData {
|
|
|
|
|
pub state: Option<RestoreTilingState>,
|
|
|
|
|
pub was_maximized: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct FullscreenRestoreData {
|
|
|
|
|
pub previous_state: FullscreenRestoreState,
|
|
|
|
|
pub previous_geometry: Rectangle<i32, Local>,
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-20 16:30:37 +02:00
|
|
|
#[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,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-20 18:57:58 +02:00
|
|
|
#[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,
|
2023-10-10 13:55:34 -04:00
|
|
|
theme: cosmic::Theme,
|
2023-10-25 19:40:26 +02:00
|
|
|
) -> Workspace {
|
2023-12-07 19:41:28 +00:00
|
|
|
let tiling_layer = TilingLayout::new(theme.clone(), &output);
|
|
|
|
|
let floating_layer = FloatingLayout::new(theme, &output);
|
2025-01-31 14:13:33 -08:00
|
|
|
let output_match = output_match_for_output(&output);
|
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,
|
2024-02-08 20:28:59 +01:00
|
|
|
minimized_windows: Vec::new(),
|
2023-10-25 19:41:30 +02:00
|
|
|
fullscreen: None,
|
2025-01-31 14:13:33 -08:00
|
|
|
pinned: false,
|
2022-07-04 15:28:03 +02:00
|
|
|
handle,
|
2022-09-28 12:01:29 +02:00
|
|
|
focus_stack: FocusStacks::default(),
|
2024-03-12 19:42:48 +01:00
|
|
|
screencopy: ScreencopySessions::default(),
|
2023-11-20 19:11:02 +01:00
|
|
|
output_stack: {
|
|
|
|
|
let mut queue = VecDeque::new();
|
2025-03-26 12:54:46 -07:00
|
|
|
queue.push_back(output_match);
|
2023-11-20 19:11:02 +01:00
|
|
|
queue
|
|
|
|
|
},
|
2023-05-26 20:51:10 +02:00
|
|
|
backdrop_id: Id::new(),
|
2023-09-20 14:56:18 +02:00
|
|
|
dirty: AtomicBool::new(false),
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-31 14:13:33 -08:00
|
|
|
pub fn from_pinned(
|
|
|
|
|
pinned: &PinnedWorkspace,
|
|
|
|
|
handle: WorkspaceHandle,
|
|
|
|
|
output: Output,
|
|
|
|
|
theme: cosmic::Theme,
|
|
|
|
|
) -> Self {
|
|
|
|
|
let tiling_layer = TilingLayout::new(theme.clone(), &output);
|
|
|
|
|
let floating_layer = FloatingLayout::new(theme, &output);
|
|
|
|
|
let output_match = output_match_for_output(&output);
|
|
|
|
|
|
|
|
|
|
Workspace {
|
|
|
|
|
output,
|
|
|
|
|
tiling_layer,
|
|
|
|
|
floating_layer,
|
|
|
|
|
tiling_enabled: pinned.tiling_enabled,
|
|
|
|
|
minimized_windows: Vec::new(),
|
|
|
|
|
fullscreen: None,
|
|
|
|
|
pinned: true,
|
|
|
|
|
handle,
|
|
|
|
|
focus_stack: FocusStacks::default(),
|
|
|
|
|
screencopy: ScreencopySessions::default(),
|
|
|
|
|
output_stack: {
|
|
|
|
|
let mut queue = VecDeque::new();
|
|
|
|
|
queue.push_back(pinned.output.clone());
|
|
|
|
|
if output_match != pinned.output {
|
|
|
|
|
queue.push_back(output_match);
|
|
|
|
|
}
|
|
|
|
|
queue
|
|
|
|
|
},
|
|
|
|
|
backdrop_id: Id::new(),
|
|
|
|
|
dirty: AtomicBool::new(false),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn to_pinned(&self) -> Option<PinnedWorkspace> {
|
|
|
|
|
let output = self.explicit_output().clone();
|
|
|
|
|
if self.pinned {
|
|
|
|
|
Some(PinnedWorkspace {
|
|
|
|
|
output: cosmic_comp_config::workspace::OutputMatch {
|
|
|
|
|
name: output.name,
|
|
|
|
|
edid: output.edid,
|
|
|
|
|
},
|
|
|
|
|
tiling_enabled: self.tiling_enabled,
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-07 19:15:44 -07:00
|
|
|
#[profiling::function]
|
2025-03-13 12:50:02 -07:00
|
|
|
pub fn refresh(&mut self) {
|
2025-06-25 17:54:27 +02:00
|
|
|
self.fullscreen.take_if(|w| !w.alive());
|
2022-09-28 12:01:29 +02:00
|
|
|
self.floating_layer.refresh();
|
|
|
|
|
self.tiling_layer.refresh();
|
2025-03-13 12:50:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn has_activation_token(&self, xdg_activation_state: &XdgActivationState) -> bool {
|
|
|
|
|
xdg_activation_state.tokens().any(|(_, data)| {
|
|
|
|
|
if let ActivationContext::Workspace(handle) =
|
|
|
|
|
data.user_data.get::<ActivationContext>().unwrap()
|
|
|
|
|
{
|
|
|
|
|
*handle == self.handle
|
|
|
|
|
&& data.timestamp.elapsed() < super::ACTIVATION_TOKEN_EXPIRE_TIME
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
2023-11-07 18:46:25 +01:00
|
|
|
|
2025-03-13 12:50:02 -07:00
|
|
|
// Auto-removal of workspaces is allowed if empty, unless blocked by an
|
2025-01-31 14:13:33 -08:00
|
|
|
// unused and unexpired activation token, or pinned.
|
2025-03-13 12:50:02 -07:00
|
|
|
pub fn can_auto_remove(&self, xdg_activation_state: &XdgActivationState) -> bool {
|
2025-01-31 14:13:33 -08:00
|
|
|
self.is_empty() && !self.has_activation_token(xdg_activation_state) && !self.pinned
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2023-09-11 21:11:34 +02:00
|
|
|
pub fn refresh_focus_stack(&mut self) {
|
|
|
|
|
for stack in self.focus_stack.0.values_mut() {
|
2025-06-25 17:54:27 +02:00
|
|
|
let fullscreen = self
|
|
|
|
|
.fullscreen
|
|
|
|
|
.as_ref()
|
|
|
|
|
.filter(|f| f.alive())
|
|
|
|
|
.filter(|f| f.ended_at.is_none())
|
|
|
|
|
.map(|f| &f.surface);
|
|
|
|
|
let mapped = || {
|
|
|
|
|
self.floating_layer
|
|
|
|
|
.mapped()
|
|
|
|
|
.chain(self.tiling_layer.mapped().map(|(w, _)| w))
|
|
|
|
|
};
|
|
|
|
|
stack.retain(|w| match w {
|
|
|
|
|
FocusTarget::Fullscreen(s) => fullscreen.is_some_and(|f| f == s),
|
|
|
|
|
FocusTarget::Window(w) => mapped().any(|m| w == m),
|
|
|
|
|
});
|
2023-09-11 21:11:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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())
|
2023-09-20 14:56:18 +02:00
|
|
|
|| 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> {
|
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();
|
2023-09-20 14:56:18 +02:00
|
|
|
self.dirty.store(true, Ordering::SeqCst);
|
2023-09-15 18:37:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-09-20 14:56:18 +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);
|
|
|
|
|
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-20 14:56:18 +02:00
|
|
|
}
|
2023-09-15 18:37:34 +02:00
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
let clients = self.tiling_layer.update_animation_state();
|
2023-11-14 16:32:49 +01:00
|
|
|
self.floating_layer.update_animation_state();
|
2023-09-20 14:56:18 +02:00
|
|
|
clients
|
2023-05-12 20:01:37 +02:00
|
|
|
}
|
|
|
|
|
|
2023-10-25 19:40:26 +02:00
|
|
|
pub fn output(&self) -> &Output {
|
|
|
|
|
&self.output
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2025-01-31 14:13:33 -08:00
|
|
|
/// Output workspace was originally created on, or explicitly moved to by the user
|
|
|
|
|
fn explicit_output(&self) -> &OutputMatch {
|
|
|
|
|
self.output_stack.front().unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-26 12:08:46 -07:00
|
|
|
// Set output the workspace is on
|
|
|
|
|
//
|
|
|
|
|
// If `explicit` is `true`, the user has explicitly moved the workspace
|
|
|
|
|
// to this output, so previous outputs it was on can be forgotten.
|
|
|
|
|
pub fn set_output(&mut self, output: &Output, explicit: bool) {
|
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() {
|
2024-04-10 15:49:08 +02:00
|
|
|
toplevel_leave_output(&surface, &self.output);
|
|
|
|
|
toplevel_enter_output(&surface, output);
|
2023-10-25 19:40:26 +02:00
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
2024-02-23 17:25:40 +01:00
|
|
|
for window in self.minimized_windows.iter() {
|
2025-06-25 17:54:27 +02:00
|
|
|
for surface in window.windows() {
|
2024-04-10 15:49:08 +02:00
|
|
|
toplevel_leave_output(&surface, &self.output);
|
|
|
|
|
toplevel_enter_output(&surface, output);
|
2024-02-23 17:25:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-06-25 17:54:27 +02:00
|
|
|
if let Some(f) = self.fullscreen.as_ref().filter(|f| f.ended_at.is_none()) {
|
|
|
|
|
toplevel_leave_output(&f.surface, &self.output);
|
|
|
|
|
toplevel_enter_output(&f.surface, output);
|
|
|
|
|
}
|
2025-03-26 12:08:46 -07:00
|
|
|
if explicit {
|
|
|
|
|
self.output_stack.clear();
|
|
|
|
|
}
|
2025-04-01 13:38:20 -07:00
|
|
|
if let Some(pos) = self
|
|
|
|
|
.output_stack
|
|
|
|
|
.iter()
|
2025-01-31 14:13:33 -08:00
|
|
|
.position(|i| output_matches(i, output, true))
|
2025-04-01 13:38:20 -07:00
|
|
|
{
|
|
|
|
|
// Matched edid and connector name
|
2023-11-20 19:11:02 +01:00
|
|
|
self.output_stack.truncate(pos + 1);
|
2025-04-01 13:38:20 -07:00
|
|
|
} else if let Some(pos) = self
|
|
|
|
|
.output_stack
|
|
|
|
|
.iter()
|
2025-01-31 14:13:33 -08:00
|
|
|
.position(|i| output_matches(i, output, false))
|
2025-04-01 13:38:20 -07:00
|
|
|
{
|
|
|
|
|
// Matched edid but not connector name; truncate entries that don't match edid,
|
|
|
|
|
// but keep old entry in case we see two outputs with the same edid.
|
|
|
|
|
self.output_stack.truncate(pos + 1);
|
2025-01-31 14:13:33 -08:00
|
|
|
self.output_stack.push_back(output_match_for_output(output));
|
2023-11-20 19:11:02 +01:00
|
|
|
} else {
|
2025-01-31 14:13:33 -08:00
|
|
|
self.output_stack.push_back(output_match_for_output(output));
|
2023-11-20 19:11:02 +01:00
|
|
|
}
|
2023-10-25 19:40:26 +02:00
|
|
|
self.output = output.clone();
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-11 12:20:06 -07:00
|
|
|
pub fn prefers_output(&self, output: &Output) -> bool {
|
2025-04-01 13:38:20 -07:00
|
|
|
// Disambiguate match by connector name if existing output has same edid
|
|
|
|
|
let disambiguate = output
|
|
|
|
|
.edid()
|
|
|
|
|
.is_some_and(|edid| self.output().edid() == Some(edid));
|
|
|
|
|
self.output_stack
|
|
|
|
|
.iter()
|
2025-01-31 14:13:33 -08:00
|
|
|
.any(|i| output_matches(i, output, disambiguate))
|
2023-11-20 19:11:02 +01:00
|
|
|
}
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
pub fn unmap_element(&mut self, mapped: &CosmicMapped) -> Option<WorkspaceRestoreData> {
|
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
|
2025-06-25 17:54:27 +02:00
|
|
|
let _ = self.unmaximize_request(&mapped);
|
2023-10-25 19:41:30 +02:00
|
|
|
}
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
self.focus_stack
|
|
|
|
|
.0
|
|
|
|
|
.values_mut()
|
|
|
|
|
.for_each(|set| set.retain(|m| m != mapped));
|
|
|
|
|
|
|
|
|
|
if let Some(pos) = self.minimized_windows.iter().position(|m| m == mapped) {
|
2024-02-23 17:25:40 +01:00
|
|
|
let state = self.minimized_windows.remove(pos);
|
2025-06-25 17:54:27 +02:00
|
|
|
return Some(match state {
|
|
|
|
|
MinimizedWindow::Floating { previous, .. } => {
|
|
|
|
|
WorkspaceRestoreData::Floating(Some(previous))
|
|
|
|
|
}
|
|
|
|
|
MinimizedWindow::Tiling { previous, .. } => {
|
|
|
|
|
WorkspaceRestoreData::Tiling(Some(previous))
|
2024-02-23 17:25:40 +01:00
|
|
|
}
|
2025-06-25 17:54:27 +02:00
|
|
|
MinimizedWindow::Fullscreen { .. } => unreachable!(),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let was_maximized =
|
|
|
|
|
if let Some(floating_geometry) = self.floating_layer.unmap(&mapped, None) {
|
|
|
|
|
if mapped.maximized_state.lock().unwrap().is_none() {
|
|
|
|
|
return Some(WorkspaceRestoreData::Floating(Some(FloatingRestoreData {
|
|
|
|
|
geometry: floating_geometry,
|
|
|
|
|
output_size: self.output.geometry().size.as_logical(),
|
|
|
|
|
})));
|
2024-02-23 17:25:40 +01:00
|
|
|
}
|
2025-06-25 17:54:27 +02:00
|
|
|
true
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
};
|
|
|
|
|
if let Ok(state) = self.tiling_layer.unmap(&mapped, None) {
|
|
|
|
|
return Some(WorkspaceRestoreData::Tiling(Some(TilingRestoreData {
|
|
|
|
|
state,
|
|
|
|
|
was_maximized,
|
|
|
|
|
})));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn unmap_surface<S>(&mut self, surface: &S) -> Option<(CosmicSurface, WorkspaceRestoreData)>
|
|
|
|
|
where
|
|
|
|
|
CosmicSurface: PartialEq<S>,
|
|
|
|
|
{
|
|
|
|
|
if self
|
|
|
|
|
.fullscreen
|
|
|
|
|
.as_ref()
|
|
|
|
|
.is_some_and(|f| f.ended_at.is_none() && &f.surface == surface)
|
|
|
|
|
{
|
|
|
|
|
let (surface, previous_state, previous_geometry) = self.remove_fullscreen().unwrap();
|
|
|
|
|
return Some((
|
|
|
|
|
surface,
|
|
|
|
|
WorkspaceRestoreData::Fullscreen(previous_state.zip(previous_geometry).map(
|
|
|
|
|
|(previous_state, previous_geometry)| FullscreenRestoreData {
|
|
|
|
|
previous_state,
|
|
|
|
|
previous_geometry,
|
|
|
|
|
},
|
|
|
|
|
)),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(pos) = self.minimized_windows.iter().position(|m| {
|
|
|
|
|
if let MinimizedWindow::Fullscreen { surface: s, .. } = m {
|
|
|
|
|
s == surface
|
|
|
|
|
} else {
|
|
|
|
|
false
|
2024-02-23 17:25:40 +01:00
|
|
|
}
|
2025-06-25 17:54:27 +02:00
|
|
|
}) {
|
|
|
|
|
let MinimizedWindow::Fullscreen { surface, previous } =
|
|
|
|
|
self.minimized_windows.remove(pos)
|
|
|
|
|
else {
|
|
|
|
|
unreachable!()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return Some((surface, WorkspaceRestoreData::Fullscreen(previous)));
|
2024-02-23 17:25:40 +01:00
|
|
|
}
|
2023-01-27 19:59:54 +01:00
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
let Some(mapped) = self.element_for_surface(surface) else {
|
|
|
|
|
return None;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let maybe_stack = mapped.stack_ref().filter(|s| s.len() > 1);
|
|
|
|
|
if let Some(stack) = maybe_stack {
|
|
|
|
|
if stack.len() > 1 {
|
|
|
|
|
let idx = stack.surfaces().position(|s| &s == surface);
|
|
|
|
|
let layer = self
|
|
|
|
|
.is_tiled(surface)
|
|
|
|
|
.then_some(ManagedLayer::Tiling)
|
|
|
|
|
.unwrap_or(ManagedLayer::Floating);
|
|
|
|
|
return idx
|
|
|
|
|
.and_then(|idx| stack.remove_idx(idx))
|
|
|
|
|
.map(|s| (s, layer.into()));
|
|
|
|
|
}
|
2022-11-14 11:54:22 +01:00
|
|
|
}
|
2025-06-25 17:54:27 +02:00
|
|
|
|
|
|
|
|
// we know mapped is no stack with more than one element now,
|
|
|
|
|
// so we can treat mapped as containing only our surface.
|
|
|
|
|
|
|
|
|
|
let mapped = mapped.clone();
|
|
|
|
|
let layer = self.unmap_element(&mapped)?;
|
|
|
|
|
Some((mapped.active_window(), layer))
|
2022-11-14 11:54:22 +01:00
|
|
|
}
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
pub fn fullscreen_geometry(&self) -> Option<Rectangle<i32, Local>> {
|
2024-10-10 23:18:04 +02:00
|
|
|
self.fullscreen.as_ref().map(|fullscreen| {
|
|
|
|
|
let bbox = fullscreen.surface.bbox().as_local();
|
|
|
|
|
|
2024-12-26 18:18:35 -08:00
|
|
|
let mut full_geo = Rectangle::from_size(self.output.geometry().size.as_local());
|
2024-10-10 23:18:04 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
full_geo
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-21 12:44:40 +01:00
|
|
|
pub fn element_for_surface<S>(&self, surface: &S) -> Option<&CosmicMapped>
|
|
|
|
|
where
|
|
|
|
|
CosmicSurface: PartialEq<S>,
|
|
|
|
|
{
|
2023-01-18 20:23:41 +01:00
|
|
|
self.floating_layer
|
|
|
|
|
.mapped()
|
2024-02-28 19:49:36 +01:00
|
|
|
.chain(self.tiling_layer.mapped().map(|(w, _)| w))
|
2025-06-25 17:54:27 +02:00
|
|
|
.chain(self.minimized_windows.iter().flat_map(|w| w.mapped()))
|
2023-01-18 20:23:41 +01:00
|
|
|
.find(|e| e.windows().any(|(w, _)| &w == surface))
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
pub fn popup_element_under(
|
|
|
|
|
&self,
|
|
|
|
|
location: Point<f64, Global>,
|
|
|
|
|
seat: &Seat<State>,
|
|
|
|
|
) -> Option<KeyboardFocusTarget> {
|
2024-10-10 23:18:04 +02:00
|
|
|
if !self.output.geometry().contains(location.to_i32_round()) {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
2024-03-25 21:48:34 +01:00
|
|
|
let location = location.to_local(&self.output);
|
2024-10-10 23:18:04 +02:00
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
let fullscreen_element_under =
|
|
|
|
|
|fullscreen: &FullscreenSurface, geometry: Rectangle<i32, Local>| {
|
|
|
|
|
fullscreen
|
2024-10-10 23:18:04 +02:00
|
|
|
.surface
|
|
|
|
|
.0
|
|
|
|
|
.surface_under(
|
2025-06-25 17:54:27 +02:00
|
|
|
(location - geometry.loc.to_f64()).as_logical(),
|
2024-10-10 23:18:04 +02:00
|
|
|
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
|
|
|
|
|
)
|
|
|
|
|
.is_some()
|
2025-06-25 17:54:27 +02:00
|
|
|
.then(|| KeyboardFocusTarget::Fullscreen(fullscreen.surface.clone()))
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let stack = self.focus_stack.get(seat);
|
|
|
|
|
let last_focused = stack.last();
|
|
|
|
|
|
|
|
|
|
if let Some(fullscreen) = self.fullscreen.as_ref() {
|
|
|
|
|
if last_focused.is_some_and(
|
|
|
|
|
|t| matches!(t, FocusTarget::Fullscreen(f) if f == &fullscreen.surface),
|
|
|
|
|
) && !fullscreen.is_animating()
|
|
|
|
|
{
|
|
|
|
|
let geometry = self.fullscreen_geometry().unwrap();
|
|
|
|
|
return fullscreen_element_under(fullscreen, geometry);
|
2024-10-10 23:18:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-25 21:48:34 +01:00
|
|
|
self.floating_layer
|
2024-10-10 23:18:04 +02:00
|
|
|
.popup_element_under(location)
|
|
|
|
|
.or_else(|| self.tiling_layer.popup_element_under(location))
|
2025-06-25 17:54:27 +02:00
|
|
|
.or_else(|| {
|
|
|
|
|
if last_focused.is_none_or(|t| !matches!(t, FocusTarget::Fullscreen(_))) {
|
|
|
|
|
if let Some(fullscreen) = self.fullscreen.as_ref() {
|
|
|
|
|
let geometry = self.fullscreen_geometry().unwrap();
|
|
|
|
|
return fullscreen_element_under(fullscreen, geometry);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
})
|
2024-03-25 21:48:34 +01:00
|
|
|
}
|
|
|
|
|
|
2024-10-10 23:18:04 +02:00
|
|
|
pub fn toplevel_element_under(
|
|
|
|
|
&self,
|
|
|
|
|
location: Point<f64, Global>,
|
2025-06-25 17:54:27 +02:00
|
|
|
seat: &Seat<State>,
|
2024-10-10 23:18:04 +02:00
|
|
|
) -> Option<KeyboardFocusTarget> {
|
|
|
|
|
if !self.output.geometry().contains(location.to_i32_round()) {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
let location = location.to_local(&self.output);
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
let fullscreen_element_under =
|
|
|
|
|
|fullscreen: &FullscreenSurface, geometry: Rectangle<i32, Local>| {
|
|
|
|
|
fullscreen
|
2024-10-10 23:18:04 +02:00
|
|
|
.surface
|
|
|
|
|
.0
|
|
|
|
|
.surface_under(
|
2025-06-25 17:54:27 +02:00
|
|
|
(location - geometry.loc.to_f64()).as_logical(),
|
2024-10-10 23:18:04 +02:00
|
|
|
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
|
|
|
|
|
)
|
|
|
|
|
.is_some()
|
2025-06-25 17:54:27 +02:00
|
|
|
.then(|| KeyboardFocusTarget::Fullscreen(fullscreen.surface.clone()))
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let stack = self.focus_stack.get(seat);
|
|
|
|
|
let last_focused = stack.last();
|
|
|
|
|
|
|
|
|
|
if let Some(fullscreen) = self.fullscreen.as_ref() {
|
|
|
|
|
if last_focused.is_some_and(
|
|
|
|
|
|t| matches!(t, FocusTarget::Fullscreen(f) if f == &fullscreen.surface),
|
|
|
|
|
) && !fullscreen.is_animating()
|
|
|
|
|
{
|
|
|
|
|
let geometry = self.fullscreen_geometry().unwrap();
|
|
|
|
|
return fullscreen_element_under(fullscreen, geometry);
|
2024-10-10 23:18:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.floating_layer
|
|
|
|
|
.toplevel_element_under(location)
|
|
|
|
|
.or_else(|| self.tiling_layer.toplevel_element_under(location))
|
2025-06-25 17:54:27 +02:00
|
|
|
.or_else(|| {
|
|
|
|
|
if !last_focused.is_none_or(|t| !matches!(t, FocusTarget::Fullscreen(_))) {
|
|
|
|
|
if let Some(fullscreen) = self.fullscreen.as_ref() {
|
|
|
|
|
let geometry = self.fullscreen_geometry().unwrap();
|
|
|
|
|
return fullscreen_element_under(fullscreen, geometry);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
})
|
2024-10-10 23:18:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn popup_surface_under(
|
|
|
|
|
&self,
|
2023-10-25 19:40:26 +02:00
|
|
|
location: Point<f64, Global>,
|
2023-07-21 16:08:55 +02:00
|
|
|
overview: OverviewMode,
|
2025-06-25 17:54:27 +02:00
|
|
|
seat: &Seat<State>,
|
2024-06-18 19:23:16 -07:00
|
|
|
) -> Option<(PointerFocusTarget, Point<f64, Global>)> {
|
2024-10-10 23:18:04 +02:00
|
|
|
if !self.output.geometry().contains(location.to_i32_round()) {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
let location = location.to_local(&self.output);
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
let check_fullscreen = |fullscreen: &FullscreenSurface| {
|
2024-10-10 23:18:04 +02:00
|
|
|
if !fullscreen.is_animating() {
|
|
|
|
|
let geometry = self.fullscreen_geometry().unwrap();
|
|
|
|
|
return fullscreen
|
|
|
|
|
.surface
|
|
|
|
|
.0
|
|
|
|
|
.surface_under(
|
2025-06-25 17:54:27 +02:00
|
|
|
(location - geometry.loc.to_f64()).as_logical(),
|
2024-10-10 23:18:04 +02:00
|
|
|
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
|
|
|
|
|
)
|
|
|
|
|
.map(|(surface, surface_offset)| {
|
|
|
|
|
(
|
|
|
|
|
PointerFocusTarget::WlSurface {
|
|
|
|
|
surface,
|
|
|
|
|
toplevel: Some(fullscreen.surface.clone().into()),
|
|
|
|
|
},
|
2025-06-25 17:54:27 +02:00
|
|
|
(geometry.loc + surface_offset.as_local()).to_f64(),
|
2024-10-10 23:18:04 +02:00
|
|
|
)
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-06-25 17:54:27 +02:00
|
|
|
None
|
|
|
|
|
};
|
2024-10-10 23:18:04 +02:00
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
let stack = self.focus_stack.get(seat);
|
|
|
|
|
let last_focused = stack.last();
|
|
|
|
|
|
|
|
|
|
self.fullscreen
|
|
|
|
|
.as_ref()
|
|
|
|
|
.filter(|f| last_focused.is_some_and(|t| t == &f.surface))
|
|
|
|
|
.and_then(|f| check_fullscreen(f))
|
|
|
|
|
.or_else(|| self.floating_layer.popup_surface_under(location))
|
2024-10-10 23:18:04 +02:00
|
|
|
.or_else(|| self.tiling_layer.popup_surface_under(location, overview))
|
2025-06-25 17:54:27 +02:00
|
|
|
.or_else(|| {
|
|
|
|
|
self.fullscreen
|
|
|
|
|
.as_ref()
|
|
|
|
|
.filter(|f| last_focused.is_none_or(|t| t != &f.surface))
|
|
|
|
|
.and_then(|f| check_fullscreen(f))
|
|
|
|
|
})
|
2024-10-10 23:18:04 +02:00
|
|
|
.map(|(m, p)| (m, p.to_global(&self.output)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn toplevel_surface_under(
|
|
|
|
|
&self,
|
|
|
|
|
location: Point<f64, Global>,
|
|
|
|
|
overview: OverviewMode,
|
2025-06-25 17:54:27 +02:00
|
|
|
seat: &Seat<State>,
|
2024-10-10 23:18:04 +02:00
|
|
|
) -> Option<(PointerFocusTarget, Point<f64, Global>)> {
|
|
|
|
|
if !self.output.geometry().contains(location.to_i32_round()) {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
2023-10-25 19:40:26 +02:00
|
|
|
let location = location.to_local(&self.output);
|
2024-10-10 23:18:04 +02:00
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
let check_fullscreen = |fullscreen: &FullscreenSurface| {
|
2024-10-10 23:18:04 +02:00
|
|
|
if !fullscreen.is_animating() {
|
|
|
|
|
let geometry = self.fullscreen_geometry().unwrap();
|
|
|
|
|
return fullscreen
|
|
|
|
|
.surface
|
|
|
|
|
.0
|
|
|
|
|
.surface_under(
|
2025-06-25 17:54:27 +02:00
|
|
|
(location - geometry.loc.to_f64()).as_logical(),
|
2024-10-10 23:18:04 +02:00
|
|
|
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
|
|
|
|
|
)
|
|
|
|
|
.map(|(surface, surface_offset)| {
|
|
|
|
|
(
|
|
|
|
|
PointerFocusTarget::WlSurface {
|
|
|
|
|
surface,
|
|
|
|
|
toplevel: Some(fullscreen.surface.clone().into()),
|
|
|
|
|
},
|
2025-06-25 17:54:27 +02:00
|
|
|
(geometry.loc + surface_offset.as_local()).to_f64(),
|
2024-10-10 23:18:04 +02:00
|
|
|
)
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
None
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let stack = self.focus_stack.get(seat);
|
|
|
|
|
let last_focused = stack.last();
|
|
|
|
|
|
|
|
|
|
self.fullscreen
|
|
|
|
|
.as_ref()
|
|
|
|
|
.filter(|f| last_focused.is_some_and(|t| t == &f.surface))
|
|
|
|
|
.and_then(|f| check_fullscreen(f))
|
|
|
|
|
.or_else(|| self.floating_layer.toplevel_surface_under(location))
|
2024-10-10 23:18:04 +02:00
|
|
|
.or_else(|| self.tiling_layer.toplevel_surface_under(location, overview))
|
2025-06-25 17:54:27 +02:00
|
|
|
.or_else(|| {
|
|
|
|
|
self.fullscreen
|
|
|
|
|
.as_ref()
|
|
|
|
|
.filter(|f| last_focused.is_none_or(|t| t != &f.surface))
|
|
|
|
|
.and_then(|f| check_fullscreen(f))
|
|
|
|
|
})
|
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
|
|
|
}
|
|
|
|
|
|
2024-10-10 23:18:04 +02:00
|
|
|
pub fn update_pointer_position(
|
|
|
|
|
&mut self,
|
|
|
|
|
location: Option<Point<f64, Local>>,
|
|
|
|
|
overview: OverviewMode,
|
|
|
|
|
) {
|
|
|
|
|
self.floating_layer.update_pointer_position(location);
|
|
|
|
|
self.tiling_layer
|
|
|
|
|
.update_pointer_position(location, overview);
|
|
|
|
|
}
|
|
|
|
|
|
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();
|
2024-06-27 13:32:17 +02:00
|
|
|
self.floating_layer.recalculate();
|
2023-09-13 20:14:54 +02:00
|
|
|
}
|
|
|
|
|
|
2023-12-20 20:45:47 +00:00
|
|
|
pub fn unmaximize_request(&mut self, elem: &CosmicMapped) -> Option<Size<i32, Logical>> {
|
2023-12-20 20:49:37 +00:00
|
|
|
let mut state = elem.maximized_state.lock().unwrap();
|
|
|
|
|
if let Some(state) = state.take() {
|
2025-06-25 17:54:27 +02:00
|
|
|
if let Some(minimized) = self.minimized_windows.iter_mut().find(|m| *m == elem) {
|
2024-02-23 17:25:40 +01:00
|
|
|
minimized.unmaximize(state.original_geometry);
|
|
|
|
|
Some(state.original_geometry.size.as_logical())
|
|
|
|
|
} else {
|
|
|
|
|
match state.original_layer {
|
|
|
|
|
ManagedLayer::Tiling if self.tiling_enabled => {
|
|
|
|
|
// should still be mapped in tiling
|
2025-06-25 17:54:27 +02:00
|
|
|
self.floating_layer.unmap(&elem, None);
|
2024-02-23 17:25:40 +01:00
|
|
|
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()),
|
|
|
|
|
None,
|
|
|
|
|
);
|
|
|
|
|
Some(state.original_geometry.size.as_logical())
|
|
|
|
|
}
|
2023-12-20 20:49:37 +00:00
|
|
|
}
|
2024-02-23 17:25:40 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
pub fn minimize<S>(&mut self, surface: &S, to: Rectangle<i32, Local>) -> Option<MinimizedWindow>
|
|
|
|
|
where
|
|
|
|
|
CosmicSurface: PartialEq<S>,
|
|
|
|
|
{
|
|
|
|
|
if self.get_fullscreen().is_some_and(|s| s == surface) {
|
2024-02-23 17:25:40 +01:00
|
|
|
let fullscreen_state = self.fullscreen.clone().unwrap();
|
|
|
|
|
{
|
|
|
|
|
let f = self.fullscreen.as_mut().unwrap();
|
2025-06-25 17:54:27 +02:00
|
|
|
f.previous_geometry = Some(to);
|
2024-02-23 17:25:40 +01: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)),
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-06-30 19:54:09 +02:00
|
|
|
|
|
|
|
|
fullscreen_state.surface.set_minimized(true);
|
2025-06-25 17:54:27 +02:00
|
|
|
return Some(MinimizedWindow::Fullscreen {
|
|
|
|
|
surface: fullscreen_state.surface,
|
|
|
|
|
previous: fullscreen_state
|
|
|
|
|
.previous_state
|
|
|
|
|
.zip(fullscreen_state.previous_geometry)
|
|
|
|
|
.map(
|
|
|
|
|
|(previous_state, previous_geometry)| FullscreenRestoreData {
|
|
|
|
|
previous_state,
|
|
|
|
|
previous_geometry,
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-02-23 17:25:40 +01:00
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
let maybe_elem = self
|
|
|
|
|
.tiling_layer
|
|
|
|
|
.mapped()
|
|
|
|
|
.find(|(m, _)| m.windows().any(|(ref s, _)| s == surface))
|
|
|
|
|
.map(|(s, _)| s.clone());
|
|
|
|
|
if let Some(elem) = maybe_elem {
|
|
|
|
|
let was_maximized = self.floating_layer.unmap(&elem, None).is_some();
|
|
|
|
|
let previous_state = self.tiling_layer.unmap(&elem, Some(to)).unwrap();
|
2025-06-30 19:54:09 +02:00
|
|
|
elem.set_minimized(true);
|
2025-06-25 17:54:27 +02:00
|
|
|
return Some(MinimizedWindow::Tiling {
|
|
|
|
|
window: elem,
|
|
|
|
|
previous: TilingRestoreData {
|
|
|
|
|
state: previous_state,
|
2024-02-23 17:25:40 +01:00
|
|
|
was_maximized,
|
|
|
|
|
},
|
2025-06-25 17:54:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let maybe_elem = self
|
|
|
|
|
.floating_layer
|
|
|
|
|
.mapped()
|
|
|
|
|
.find(|m| m.windows().any(|(ref s, _)| s == surface))
|
|
|
|
|
.cloned();
|
|
|
|
|
if let Some(elem) = maybe_elem {
|
|
|
|
|
let geometry = self.floating_layer.unmap(&elem, Some(to)).unwrap();
|
2025-06-30 19:54:09 +02:00
|
|
|
elem.set_minimized(true);
|
2025-06-25 17:54:27 +02:00
|
|
|
return Some(MinimizedWindow::Floating {
|
|
|
|
|
window: elem,
|
|
|
|
|
previous: FloatingRestoreData {
|
|
|
|
|
geometry,
|
|
|
|
|
output_size: self.output.geometry().size.as_logical(),
|
|
|
|
|
},
|
|
|
|
|
});
|
2024-02-23 17:25:40 +01:00
|
|
|
}
|
2025-06-25 17:54:27 +02:00
|
|
|
|
|
|
|
|
None
|
2024-02-23 17:25:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn unminimize(
|
|
|
|
|
&mut self,
|
|
|
|
|
window: MinimizedWindow,
|
|
|
|
|
from: Rectangle<i32, Local>,
|
|
|
|
|
seat: &Seat<State>,
|
2025-06-25 17:54:27 +02:00
|
|
|
) -> Option<(
|
|
|
|
|
CosmicSurface,
|
|
|
|
|
Option<FullscreenRestoreState>,
|
|
|
|
|
Option<Rectangle<i32, Local>>,
|
|
|
|
|
)> {
|
|
|
|
|
match window {
|
|
|
|
|
MinimizedWindow::Fullscreen { previous, surface } => {
|
|
|
|
|
let old_fullscreen = self.remove_fullscreen();
|
2025-06-30 19:54:09 +02:00
|
|
|
surface.set_minimized(false);
|
2025-06-25 17:54:27 +02:00
|
|
|
self.fullscreen = Some(FullscreenSurface {
|
|
|
|
|
surface,
|
|
|
|
|
previous_state: previous.clone().map(|p| p.previous_state),
|
|
|
|
|
previous_geometry: previous.map(|p| p.previous_geometry),
|
|
|
|
|
start_at: None,
|
|
|
|
|
ended_at: None,
|
|
|
|
|
});
|
|
|
|
|
old_fullscreen
|
|
|
|
|
}
|
|
|
|
|
MinimizedWindow::Floating { window, previous } => {
|
2024-02-23 17:25:40 +01:00
|
|
|
let current_output_size = self.output.geometry().size.as_logical();
|
2025-06-25 17:54:27 +02:00
|
|
|
let previous_position = previous.position_relative(current_output_size);
|
|
|
|
|
|
2025-06-30 19:54:09 +02:00
|
|
|
window.set_minimized(false);
|
2024-02-23 17:25:40 +01:00
|
|
|
self.floating_layer
|
2025-06-25 17:54:27 +02:00
|
|
|
.remap_minimized(window, from, previous_position);
|
|
|
|
|
None
|
2024-02-23 17:25:40 +01:00
|
|
|
}
|
2025-06-25 17:54:27 +02:00
|
|
|
MinimizedWindow::Tiling {
|
|
|
|
|
window,
|
|
|
|
|
previous:
|
|
|
|
|
TilingRestoreData {
|
|
|
|
|
state,
|
|
|
|
|
was_maximized,
|
|
|
|
|
},
|
2024-02-23 17:25:40 +01:00
|
|
|
} => {
|
2025-06-30 19:54:09 +02:00
|
|
|
window.set_minimized(false);
|
2024-02-23 17:25:40 +01:00
|
|
|
if self.tiling_enabled {
|
|
|
|
|
let focus_stack = self.focus_stack.get(seat);
|
2025-06-25 17:54:27 +02:00
|
|
|
self.tiling_layer
|
|
|
|
|
.remap(window.clone(), None, state, Some(focus_stack.iter()));
|
2024-02-23 17:25:40 +01:00
|
|
|
if was_maximized {
|
|
|
|
|
let previous_geometry =
|
2025-06-25 17:54:27 +02:00
|
|
|
self.tiling_layer.element_geometry(&window).unwrap();
|
2024-02-23 17:25:40 +01:00
|
|
|
self.floating_layer
|
2025-06-25 17:54:27 +02:00
|
|
|
.map_maximized(window, previous_geometry, true);
|
2024-02-23 17:25:40 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if was_maximized {
|
2025-06-25 17:54:27 +02:00
|
|
|
self.floating_layer.map_maximized(window, from, true);
|
2024-02-23 17:25:40 +01:00
|
|
|
} else {
|
2025-06-25 17:54:27 +02:00
|
|
|
self.floating_layer.map(window.clone(), None);
|
2024-02-23 17:25:40 +01:00
|
|
|
// get the right animation
|
2025-06-25 17:54:27 +02:00
|
|
|
let geometry = self.floating_layer.element_geometry(&window).unwrap();
|
|
|
|
|
self.floating_layer
|
|
|
|
|
.remap_minimized(window.clone(), from, geometry.loc);
|
2024-02-23 17:25:40 +01:00
|
|
|
}
|
2023-10-25 19:41:30 +02:00
|
|
|
}
|
2025-06-25 17:54:27 +02:00
|
|
|
None
|
2023-12-20 20:49:37 +00:00
|
|
|
}
|
2024-02-23 17:25:40 +01:00
|
|
|
}
|
2022-07-08 15:20:29 +02:00
|
|
|
}
|
2022-03-24 20:32:31 +01:00
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
pub fn map_fullscreen<'a>(
|
2023-10-11 22:00:20 +02:00
|
|
|
&mut self,
|
|
|
|
|
window: &CosmicSurface,
|
2025-06-25 17:54:27 +02:00
|
|
|
seat: impl Into<Option<&'a Seat<State>>>,
|
|
|
|
|
restore: Option<FullscreenRestoreState>,
|
|
|
|
|
previous_geometry: Option<Rectangle<i32, Local>>,
|
|
|
|
|
) -> Option<(
|
|
|
|
|
CosmicSurface,
|
|
|
|
|
Option<FullscreenRestoreState>,
|
|
|
|
|
Option<Rectangle<i32, Local>>,
|
|
|
|
|
)> {
|
|
|
|
|
let res = self.remove_fullscreen();
|
2024-02-23 17:25:40 +01:00
|
|
|
|
2023-01-16 15:12:25 +01:00
|
|
|
window.set_fullscreen(true);
|
2025-06-25 17:54:27 +02:00
|
|
|
window.set_geometry(self.output.geometry(), 0);
|
2023-10-25 19:41:30 +02:00
|
|
|
window.send_configure();
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
if let Some(seat) = seat.into() {
|
|
|
|
|
self.focus_stack.get_mut(seat).append(window.clone());
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 19:41:30 +02:00
|
|
|
self.fullscreen = Some(FullscreenSurface {
|
|
|
|
|
surface: window.clone(),
|
2025-06-25 17:54:27 +02:00
|
|
|
previous_state: restore,
|
|
|
|
|
previous_geometry,
|
2023-10-25 19:41:30 +02:00
|
|
|
start_at: Some(Instant::now()),
|
|
|
|
|
ended_at: None,
|
|
|
|
|
});
|
2025-06-25 17:54:27 +02:00
|
|
|
|
|
|
|
|
res
|
2022-04-22 15:18:28 +02:00
|
|
|
}
|
2022-05-03 13:37:51 +02:00
|
|
|
|
2023-10-11 22:00:20 +02:00
|
|
|
#[must_use]
|
2025-06-25 17:54:27 +02:00
|
|
|
pub fn remove_fullscreen(
|
2023-10-11 22:00:20 +02:00
|
|
|
&mut self,
|
2025-06-25 17:54:27 +02:00
|
|
|
) -> Option<(
|
|
|
|
|
CosmicSurface,
|
|
|
|
|
Option<FullscreenRestoreState>,
|
|
|
|
|
Option<Rectangle<i32, Local>>,
|
|
|
|
|
)> {
|
|
|
|
|
if let Some(surface) = self.fullscreen.as_mut() {
|
|
|
|
|
if surface.surface.alive() {
|
|
|
|
|
surface.surface.set_fullscreen(false);
|
|
|
|
|
if let Some(previous_geometry) = surface.previous_geometry.as_ref() {
|
|
|
|
|
surface
|
|
|
|
|
.surface
|
|
|
|
|
.set_geometry(previous_geometry.to_global(&self.output), 0);
|
|
|
|
|
}
|
|
|
|
|
surface.surface.send_configure();
|
|
|
|
|
}
|
2023-09-15 18:37:34 +02:00
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
for focus_stack in self.focus_stack.0.values_mut() {
|
|
|
|
|
focus_stack.retain(|t| t != &surface.surface);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
surface.ended_at = Some(
|
2023-09-15 18:37:34 +02:00
|
|
|
Instant::now()
|
|
|
|
|
- (FULLSCREEN_ANIMATION_DURATION
|
2025-06-25 17:54:27 +02:00
|
|
|
- surface
|
|
|
|
|
.start_at
|
2023-09-15 18:37:34 +02:00
|
|
|
.take()
|
|
|
|
|
.map(|earlier| {
|
|
|
|
|
Instant::now()
|
|
|
|
|
.duration_since(earlier)
|
|
|
|
|
.min(FULLSCREEN_ANIMATION_DURATION)
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(FULLSCREEN_ANIMATION_DURATION)),
|
|
|
|
|
);
|
2023-09-20 18:57:58 +02:00
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
Some((
|
|
|
|
|
surface.surface.clone(),
|
|
|
|
|
surface.previous_state.clone(),
|
|
|
|
|
surface.previous_geometry.clone(),
|
|
|
|
|
))
|
2023-09-20 18:57:58 +02:00
|
|
|
} else {
|
|
|
|
|
None
|
2023-09-13 20:14:54 +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())
|
2024-07-08 20:33:25 +02:00
|
|
|
.filter(|f| f.ended_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
|
|
|
|
2023-07-06 00:02:29 +02:00
|
|
|
pub fn resize(
|
|
|
|
|
&mut self,
|
|
|
|
|
focused: &KeyboardFocusTarget,
|
|
|
|
|
direction: ResizeDirection,
|
|
|
|
|
edge: ResizeEdge,
|
|
|
|
|
amount: i32,
|
|
|
|
|
) -> bool {
|
2025-06-25 17:54:27 +02:00
|
|
|
if matches!(focused, KeyboardFocusTarget::Fullscreen(_)) {
|
|
|
|
|
return false;
|
2023-06-28 21:29:39 +02:00
|
|
|
}
|
|
|
|
|
|
2023-07-06 00:02:29 +02:00
|
|
|
if !self.floating_layer.resize(focused, direction, edge, amount) {
|
|
|
|
|
self.tiling_layer.resize(focused, direction, edge, amount)
|
|
|
|
|
} else {
|
|
|
|
|
true
|
2023-06-28 21:29:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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>,
|
|
|
|
|
) {
|
2024-03-07 12:22:39 +01:00
|
|
|
let mut maximized_windows = Vec::new();
|
2024-01-05 18:33:16 +00:00
|
|
|
if tiling {
|
2024-03-07 12:22:39 +01:00
|
|
|
let floating_windows = self.floating_layer.mapped().cloned().collect::<Vec<_>>();
|
|
|
|
|
|
|
|
|
|
for window in floating_windows.iter().filter(|w| w.is_maximized(false)) {
|
|
|
|
|
let original_geometry = {
|
|
|
|
|
let state = window.maximized_state.lock().unwrap();
|
|
|
|
|
state.as_ref().unwrap().original_geometry.clone()
|
|
|
|
|
};
|
|
|
|
|
self.unmaximize_request(&window);
|
|
|
|
|
maximized_windows.push((window.clone(), ManagedLayer::Tiling, original_geometry));
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-28 12:01:29 +02:00
|
|
|
let focus_stack = self.focus_stack.get(seat);
|
2024-03-07 12:22:39 +01:00
|
|
|
for window in floating_windows.into_iter() {
|
2025-06-25 17:54:27 +02:00
|
|
|
self.floating_layer.unmap(&window, None);
|
2023-10-11 22:00:20 +02:00
|
|
|
self.tiling_layer
|
2024-01-08 18:09:43 +00:00
|
|
|
.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()
|
2024-02-28 19:49:36 +01:00
|
|
|
.map(|(m, _)| m.clone())
|
2024-01-05 18:33:16 +00:00
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
.into_iter()
|
|
|
|
|
{
|
2024-03-07 12:22:39 +01:00
|
|
|
if window.is_maximized(false) {
|
|
|
|
|
let original_geometry = {
|
|
|
|
|
let state = window.maximized_state.lock().unwrap();
|
|
|
|
|
state.as_ref().unwrap().original_geometry.clone()
|
|
|
|
|
};
|
|
|
|
|
self.unmaximize_request(&window);
|
|
|
|
|
maximized_windows.push((
|
|
|
|
|
window.clone(),
|
|
|
|
|
ManagedLayer::Floating,
|
|
|
|
|
original_geometry,
|
|
|
|
|
));
|
|
|
|
|
}
|
2025-06-25 17:54:27 +02:00
|
|
|
let _ = self.tiling_layer.unmap(&window, None);
|
2024-01-05 18:33:16 +00:00
|
|
|
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
|
|
|
}
|
2024-03-07 12:22:39 +01:00
|
|
|
for (window, original_layer, original_geometry) in maximized_windows {
|
|
|
|
|
let mut state = window.maximized_state.lock().unwrap();
|
|
|
|
|
*state = Some(MaximizedState {
|
|
|
|
|
original_geometry,
|
|
|
|
|
original_layer,
|
|
|
|
|
});
|
|
|
|
|
std::mem::drop(state);
|
|
|
|
|
|
|
|
|
|
self.floating_layer
|
|
|
|
|
.map_maximized(window, original_geometry, false);
|
|
|
|
|
}
|
2022-07-07 22:41:17 +02:00
|
|
|
}
|
|
|
|
|
|
2023-12-07 19:34:03 +00: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 {
|
2023-12-07 19:34:03 +00:00
|
|
|
if window.is_maximized(false) {
|
2023-12-20 20:45:47 +00:00
|
|
|
self.unmaximize_request(window);
|
2023-12-07 19:34:03 +00:00
|
|
|
}
|
2024-02-28 19:49:36 +01:00
|
|
|
if self.tiling_layer.mapped().any(|(m, _)| m == window) {
|
2025-06-25 17:54:27 +02:00
|
|
|
let _ = self.tiling_layer.unmap(window, None);
|
2023-12-07 19:34:03 +00:00
|
|
|
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);
|
2025-06-25 17:54:27 +02:00
|
|
|
self.floating_layer.unmap(&window, None);
|
2023-12-07 19:34:03 +00:00
|
|
|
self.tiling_layer
|
2024-01-08 18:09:43 +00:00
|
|
|
.map(window.clone(), Some(focus_stack.iter()), None)
|
2022-07-07 22:41:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
|
2023-12-07 19:34:03 +00:00
|
|
|
pub fn toggle_floating_window_focused(&mut self, seat: &Seat<State>) {
|
2025-06-25 17:54:27 +02:00
|
|
|
if matches!(
|
|
|
|
|
seat.get_keyboard().unwrap().current_focus(),
|
|
|
|
|
Some(KeyboardFocusTarget::Fullscreen(_))
|
|
|
|
|
) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-12-07 19:34:03 +00:00
|
|
|
let maybe_window = self.focus_stack.get(seat).iter().next().cloned();
|
2025-06-25 17:54:27 +02:00
|
|
|
if let Some(FocusTarget::Window(window)) = maybe_window {
|
2023-12-07 19:34:03 +00:00
|
|
|
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()
|
2024-02-28 19:49:36 +01:00
|
|
|
.chain(self.tiling_layer.mapped().map(|(w, _)| w))
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
|
self.floating_layer.mapped().count()
|
|
|
|
|
+ self.tiling_layer.mapped().count()
|
|
|
|
|
+ self.minimized_windows.len()
|
|
|
|
|
+ if self.fullscreen.is_some() { 1 } else { 0 }
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-23 17:25:40 +01:00
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
|
self.floating_layer.mapped().next().is_none()
|
|
|
|
|
&& self.tiling_layer.mapped().next().is_none()
|
|
|
|
|
&& self.minimized_windows.is_empty()
|
2025-06-25 17:54:27 +02:00
|
|
|
&& self.fullscreen.is_none()
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
pub fn is_floating<S>(&self, surface: &S) -> bool
|
|
|
|
|
where
|
|
|
|
|
CosmicSurface: PartialEq<S>,
|
|
|
|
|
{
|
|
|
|
|
self.floating_layer
|
|
|
|
|
.mapped()
|
|
|
|
|
.any(|m| m.windows().any(|(ref s, _)| s == surface))
|
|
|
|
|
|| self.minimized_windows.iter().any(|m| {
|
|
|
|
|
if let MinimizedWindow::Floating { window, .. } = m {
|
|
|
|
|
window.windows().any(|(ref s, _)| s == surface)
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
})
|
2023-02-13 20:27:12 +01:00
|
|
|
}
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
pub fn is_tiled<S>(&self, surface: &S) -> bool
|
|
|
|
|
where
|
|
|
|
|
CosmicSurface: PartialEq<S>,
|
|
|
|
|
{
|
|
|
|
|
self.tiling_layer
|
|
|
|
|
.mapped()
|
|
|
|
|
.any(|(m, _)| m.windows().any(|(ref s, _)| s == surface))
|
|
|
|
|
|| self.minimized_windows.iter().any(|m| {
|
|
|
|
|
if let MinimizedWindow::Tiling { window, .. } = m {
|
|
|
|
|
window.windows().any(|(ref s, _)| s == surface)
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
})
|
2023-01-18 20:23:41 +01:00
|
|
|
}
|
|
|
|
|
|
2023-08-11 18:15:22 +02:00
|
|
|
pub fn node_desc(&self, focus: KeyboardFocusTarget) -> Option<NodeDesc> {
|
|
|
|
|
match focus {
|
|
|
|
|
KeyboardFocusTarget::Element(mapped) => {
|
2025-03-07 17:36:11 +01:00
|
|
|
if mapped.is_maximized(false) {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
2024-02-28 19:49:36 +01:00
|
|
|
self.tiling_layer.mapped().find_map(|(m, _)| {
|
2023-10-25 19:40:26 +02:00
|
|
|
if m == &mapped {
|
2023-08-11 18:15:22 +02:00
|
|
|
mapped
|
|
|
|
|
.tiling_node_id
|
|
|
|
|
.lock()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.clone()
|
|
|
|
|
.map(|node_id| NodeDesc {
|
|
|
|
|
handle: self.handle.clone(),
|
2024-09-10 21:10:02 +02:00
|
|
|
node: node_id.clone(),
|
2023-08-11 18:15:22 +02:00
|
|
|
stack_window: if mapped
|
|
|
|
|
.stack_ref()
|
|
|
|
|
.map(|stack| !stack.whole_stack_focused())
|
|
|
|
|
.unwrap_or(false)
|
|
|
|
|
{
|
|
|
|
|
Some(mapped.active_window())
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
},
|
2024-09-10 21:10:02 +02:00
|
|
|
focus_stack: vec![node_id],
|
2023-08-11 18:15:22 +02:00
|
|
|
})
|
2023-10-25 19:40:26 +02:00
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
2023-08-11 18:15:22 +02:00
|
|
|
})
|
|
|
|
|
}
|
2024-09-10 21:10:02 +02:00
|
|
|
KeyboardFocusTarget::Group(WindowGroup {
|
|
|
|
|
node, focus_stack, ..
|
|
|
|
|
}) => Some(NodeDesc {
|
2023-08-11 18:15:22 +02:00
|
|
|
handle: self.handle.clone(),
|
|
|
|
|
node,
|
|
|
|
|
stack_window: None,
|
2024-09-10 21:10:02 +02:00
|
|
|
focus_stack,
|
2023-08-11 18:15:22 +02:00
|
|
|
}),
|
|
|
|
|
_ => None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-07 19:15:44 -07:00
|
|
|
#[profiling::function]
|
2023-10-25 19:40:26 +02:00
|
|
|
pub fn render<'a, R>(
|
2022-09-28 12:01:29 +02:00
|
|
|
&self,
|
2022-11-28 17:48:50 +01:00
|
|
|
renderer: &mut R,
|
2025-06-25 17:54:27 +02:00
|
|
|
last_active_seat: &Seat<State>,
|
|
|
|
|
render_focus: bool,
|
2023-09-08 22:16:39 +02:00
|
|
|
overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree<Data>>)>),
|
2023-07-06 00:03:26 +02:00
|
|
|
resize_indicator: Option<(ResizeMode, ResizeIndicator)>,
|
2023-03-09 18:27:11 +01:00
|
|
|
indicator_thickness: u8,
|
2023-10-10 13:55:34 -04:00
|
|
|
theme: &CosmicTheme,
|
2024-09-27 23:41:58 +02:00
|
|
|
) -> Result<Vec<WorkspaceRenderElement<R>>, OutputNotMapped>
|
2022-09-28 12:01:29 +02:00
|
|
|
where
|
2023-01-16 15:12:25 +01:00
|
|
|
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
2025-03-11 19:14:49 +01:00
|
|
|
R::TextureId: Send + Clone + 'static,
|
2022-11-22 10:28:30 +01:00
|
|
|
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>,
|
2023-05-19 19:44:57 +02:00
|
|
|
WorkspaceRenderElement<R>: RenderElement<R>,
|
2022-09-28 12:01:29 +02:00
|
|
|
{
|
2024-09-27 23:41:58 +02:00
|
|
|
let mut elements = Vec::default();
|
2022-09-28 12:01:29 +02:00
|
|
|
|
2023-10-25 19:24:51 +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()
|
|
|
|
|
};
|
2025-06-25 17:54:27 +02:00
|
|
|
let focused = self.focus_stack.get(last_active_seat).last().cloned();
|
2022-09-28 12:01:29 +02:00
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
let mut fullscreen_elements = if let Some(fullscreen) = self.fullscreen.as_ref() {
|
|
|
|
|
let bbox = fullscreen.surface.bbox();
|
|
|
|
|
let fullscreen_geo = self.fullscreen_geometry().unwrap();
|
|
|
|
|
let previous_geo = fullscreen
|
|
|
|
|
.previous_geometry
|
|
|
|
|
.as_ref()
|
|
|
|
|
.unwrap_or(&fullscreen_geo);
|
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,
|
2025-06-25 17:54:27 +02:00
|
|
|
EaseRectangle(*previous_geo),
|
|
|
|
|
EaseRectangle(fullscreen_geo),
|
2023-09-15 18:37:34 +02:00
|
|
|
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,
|
2025-06-25 17:54:27 +02:00
|
|
|
EaseRectangle(fullscreen_geo),
|
|
|
|
|
EaseRectangle(*previous_geo),
|
2023-09-15 18:37:34 +02:00
|
|
|
duration,
|
|
|
|
|
)
|
|
|
|
|
.0,
|
|
|
|
|
ease(EaseInOutCubic, 1.0, 0.0, duration),
|
|
|
|
|
)
|
|
|
|
|
}
|
2025-06-25 17:54:27 +02:00
|
|
|
(None, None) => (fullscreen_geo, 1.0),
|
2023-09-15 18:37:34 +02:00
|
|
|
};
|
|
|
|
|
|
2023-10-25 19:24:51 +02:00
|
|
|
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,
|
|
|
|
|
};
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
fullscreen
|
|
|
|
|
.surface
|
|
|
|
|
.render_elements::<R, CosmicWindowRenderElement<R>>(
|
|
|
|
|
renderer,
|
|
|
|
|
render_loc,
|
|
|
|
|
output_scale.into(),
|
|
|
|
|
alpha,
|
|
|
|
|
)
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|elem| RescaleRenderElement::from_element(elem, render_loc, scale))
|
|
|
|
|
.map(Into::into)
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
} else {
|
|
|
|
|
Vec::new()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if matches!(focused, Some(FocusTarget::Fullscreen(_))) {
|
|
|
|
|
elements.extend(fullscreen_elements.drain(..));
|
2023-09-15 18:37:34 +02:00
|
|
|
}
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
if !matches!(focused, Some(FocusTarget::Fullscreen(_)))
|
|
|
|
|
|| self
|
|
|
|
|
.fullscreen
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|f| f.start_at.is_some() || f.ended_at.is_some())
|
|
|
|
|
.unwrap_or(true)
|
2023-09-15 18:37:34 +02:00
|
|
|
{
|
2022-09-28 12:01:29 +02:00
|
|
|
// floating surfaces
|
2023-08-11 18:15:22 +02:00
|
|
|
let alpha = match &overview.0 {
|
2023-05-19 19:44:57 +02:00
|
|
|
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
|
|
|
|
|
}
|
2023-08-11 18:15:22 +02:00
|
|
|
OverviewMode::Ended(_, ended) => {
|
2023-05-19 19:44:57 +02:00
|
|
|
((Instant::now().duration_since(*ended).as_millis()
|
|
|
|
|
/ ANIMATION_DURATION.as_millis()) as f32)
|
|
|
|
|
* 0.4
|
|
|
|
|
+ 0.6
|
|
|
|
|
}
|
2024-07-15 16:30:54 +02:00
|
|
|
OverviewMode::Active(_) => 0.6,
|
2023-05-19 19:44:57 +02:00
|
|
|
OverviewMode::None => 1.0,
|
|
|
|
|
};
|
2023-07-13 17:19:29 +02:00
|
|
|
|
2024-09-27 23:41:58 +02:00
|
|
|
elements.extend(
|
|
|
|
|
self.floating_layer
|
|
|
|
|
.render::<R>(
|
|
|
|
|
renderer,
|
2025-06-25 17:54:27 +02:00
|
|
|
focused.as_ref().and_then(|target| {
|
|
|
|
|
if let FocusTarget::Window(mapped) = target {
|
|
|
|
|
Some(mapped)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}),
|
2024-09-27 23:41:58 +02:00
|
|
|
resize_indicator.clone(),
|
|
|
|
|
indicator_thickness,
|
|
|
|
|
alpha,
|
|
|
|
|
theme,
|
|
|
|
|
)
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(WorkspaceRenderElement::from),
|
2022-09-28 12:01:29 +02:00
|
|
|
);
|
|
|
|
|
|
2023-09-08 22:16:39 +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,
|
|
|
|
|
),
|
2024-07-15 16:30:54 +02:00
|
|
|
OverviewMode::Active(_) => Some(1.0),
|
2023-09-08 22:16:39 +02:00
|
|
|
OverviewMode::Ended(_, ended) => Some(
|
|
|
|
|
1.0 - (Instant::now().duration_since(*ended).as_millis() as f64 / 100.0)
|
|
|
|
|
.min(1.0) as f32,
|
|
|
|
|
),
|
2024-07-15 16:30:54 +02:00
|
|
|
OverviewMode::None => None,
|
2023-09-08 22:16:39 +02:00
|
|
|
};
|
|
|
|
|
|
2022-09-28 12:01:29 +02:00
|
|
|
//tiling surfaces
|
2024-09-27 23:41:58 +02:00
|
|
|
elements.extend(
|
|
|
|
|
self.tiling_layer
|
|
|
|
|
.render::<R>(
|
|
|
|
|
renderer,
|
2025-06-25 17:54:27 +02:00
|
|
|
render_focus.then_some(last_active_seat),
|
2024-09-27 23:41:58 +02:00
|
|
|
zone,
|
|
|
|
|
overview,
|
|
|
|
|
resize_indicator,
|
|
|
|
|
indicator_thickness,
|
|
|
|
|
theme,
|
|
|
|
|
)?
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(WorkspaceRenderElement::from),
|
2024-07-09 15:21:16 -07:00
|
|
|
);
|
2022-09-28 12:01:29 +02:00
|
|
|
|
2023-05-26 20:51:10 +02:00
|
|
|
if let Some(alpha) = alpha {
|
2024-09-27 23:41:58 +02:00
|
|
|
elements.push(
|
2023-05-26 20:51:10 +02:00
|
|
|
Into::<CosmicMappedRenderElement<R>>::into(BackdropShader::element(
|
|
|
|
|
renderer,
|
|
|
|
|
self.backdrop_id.clone(),
|
2024-12-26 18:18:35 -08:00
|
|
|
Rectangle::from_size(self.output.geometry().size.as_local()),
|
2023-05-26 20:51:10 +02:00
|
|
|
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],
|
|
|
|
|
))
|
2023-05-19 19:44:57 +02:00
|
|
|
.into(),
|
|
|
|
|
)
|
|
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
if !matches!(focused, Some(FocusTarget::Fullscreen(_))) {
|
|
|
|
|
elements.extend(fullscreen_elements.into_iter());
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-09 15:21:16 -07:00
|
|
|
Ok(elements)
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
2024-09-27 23:41:58 +02:00
|
|
|
|
|
|
|
|
#[profiling::function]
|
|
|
|
|
pub fn render_popups<'a, R>(
|
|
|
|
|
&self,
|
|
|
|
|
renderer: &mut R,
|
2025-06-25 17:54:27 +02:00
|
|
|
last_active_seat: &Seat<State>,
|
|
|
|
|
render_focus: bool,
|
2024-09-27 23:41:58 +02:00
|
|
|
overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree<Data>>)>),
|
|
|
|
|
theme: &CosmicTheme,
|
|
|
|
|
) -> Result<Vec<WorkspaceRenderElement<R>>, OutputNotMapped>
|
|
|
|
|
where
|
|
|
|
|
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
2025-03-11 19:14:49 +01:00
|
|
|
R::TextureId: Send + Clone + 'static,
|
2024-09-27 23:41:58 +02:00
|
|
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
|
|
|
|
CosmicWindowRenderElement<R>: RenderElement<R>,
|
|
|
|
|
CosmicStackRenderElement<R>: RenderElement<R>,
|
|
|
|
|
WorkspaceRenderElement<R>: RenderElement<R>,
|
|
|
|
|
{
|
|
|
|
|
let mut elements = Vec::default();
|
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if let Some(fullscreen) = self.fullscreen.as_ref() {
|
2025-06-25 17:54:27 +02:00
|
|
|
let fullscreen_geo = self.fullscreen_geometry().unwrap();
|
|
|
|
|
let previous_geo = fullscreen
|
|
|
|
|
.previous_geometry
|
|
|
|
|
.as_ref()
|
|
|
|
|
.unwrap_or(&fullscreen_geo);
|
2024-09-27 23:41:58 +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,
|
2025-06-25 17:54:27 +02:00
|
|
|
EaseRectangle(*previous_geo),
|
|
|
|
|
EaseRectangle(fullscreen_geo),
|
2024-09-27 23:41:58 +02:00
|
|
|
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,
|
2025-06-25 17:54:27 +02:00
|
|
|
EaseRectangle(fullscreen_geo),
|
|
|
|
|
EaseRectangle(*previous_geo),
|
2024-09-27 23:41:58 +02:00
|
|
|
duration,
|
|
|
|
|
)
|
|
|
|
|
.0,
|
|
|
|
|
ease(EaseInOutCubic, 1.0, 0.0, duration),
|
|
|
|
|
)
|
|
|
|
|
}
|
2025-06-25 17:54:27 +02:00
|
|
|
(None, None) => (fullscreen_geo, 1.0),
|
2024-09-27 23:41:58 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let render_loc = target_geo
|
|
|
|
|
.loc
|
|
|
|
|
.as_logical()
|
|
|
|
|
.to_physical_precise_round(output_scale);
|
|
|
|
|
|
|
|
|
|
elements.extend(
|
|
|
|
|
fullscreen
|
|
|
|
|
.surface
|
|
|
|
|
.popup_render_elements::<R, CosmicWindowRenderElement<R>>(
|
|
|
|
|
renderer,
|
|
|
|
|
render_loc,
|
|
|
|
|
output_scale.into(),
|
|
|
|
|
alpha,
|
|
|
|
|
)
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(Into::into),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
let focus_stack = self.focus_stack.get(last_active_seat);
|
|
|
|
|
if !matches!(focus_stack.last(), Some(FocusTarget::Fullscreen(_)))
|
|
|
|
|
|| self
|
|
|
|
|
.fullscreen
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|f| f.start_at.is_some() || f.ended_at.is_some())
|
|
|
|
|
.unwrap_or(true)
|
2024-09-27 23:41:58 +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::Active(_) => 0.6,
|
|
|
|
|
OverviewMode::None => 1.0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
elements.extend(
|
|
|
|
|
self.floating_layer
|
|
|
|
|
.render_popups::<R>(renderer, alpha)
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(WorkspaceRenderElement::from),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
//tiling surfaces
|
|
|
|
|
elements.extend(
|
|
|
|
|
self.tiling_layer
|
2025-06-25 17:54:27 +02:00
|
|
|
.render_popups::<R>(
|
|
|
|
|
renderer,
|
|
|
|
|
render_focus.then_some(last_active_seat),
|
|
|
|
|
zone,
|
|
|
|
|
overview,
|
|
|
|
|
theme,
|
|
|
|
|
)?
|
2024-09-27 23:41:58 +02:00
|
|
|
.into_iter()
|
|
|
|
|
.map(WorkspaceRenderElement::from),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(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;
|
|
|
|
|
|
2022-11-22 10:28:30 +01:00
|
|
|
pub enum WorkspaceRenderElement<R>
|
|
|
|
|
where
|
2023-01-16 15:12:25 +01:00
|
|
|
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
2025-03-11 19:14:49 +01:00
|
|
|
R::TextureId: 'static,
|
2022-11-22 10:28:30 +01:00
|
|
|
{
|
2023-09-18 18:29:13 +02:00
|
|
|
OverrideRedirect(WaylandSurfaceRenderElement<R>),
|
|
|
|
|
Fullscreen(RescaleRenderElement<CosmicWindowRenderElement<R>>),
|
|
|
|
|
FullscreenPopup(CosmicWindowRenderElement<R>),
|
2022-11-22 10:28:30 +01:00
|
|
|
Window(CosmicMappedRenderElement<R>),
|
2023-05-19 19:44:57 +02:00
|
|
|
Backdrop(TextureRenderElement<GlesTexture>),
|
2022-11-22 10:28:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<R> Element for WorkspaceRenderElement<R>
|
|
|
|
|
where
|
2023-01-16 15:12:25 +01:00
|
|
|
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
2025-03-11 19:14:49 +01:00
|
|
|
R::TextureId: 'static,
|
2022-11-22 10:28:30 +01:00
|
|
|
{
|
|
|
|
|
fn id(&self) -> &smithay::backend::renderer::element::Id {
|
|
|
|
|
match self {
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.id(),
|
2023-09-15 18:37:34 +02:00
|
|
|
WorkspaceRenderElement::Fullscreen(elem) => elem.id(),
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.id(),
|
2022-11-22 10:28:30 +01:00
|
|
|
WorkspaceRenderElement::Window(elem) => elem.id(),
|
2023-05-19 19:44:57 +02:00
|
|
|
WorkspaceRenderElement::Backdrop(elem) => elem.id(),
|
2022-11-22 10:28:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn current_commit(&self) -> smithay::backend::renderer::utils::CommitCounter {
|
|
|
|
|
match self {
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.current_commit(),
|
2023-09-15 18:37:34 +02:00
|
|
|
WorkspaceRenderElement::Fullscreen(elem) => elem.current_commit(),
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.current_commit(),
|
2022-11-22 10:28:30 +01:00
|
|
|
WorkspaceRenderElement::Window(elem) => elem.current_commit(),
|
2023-05-19 19:44:57 +02:00
|
|
|
WorkspaceRenderElement::Backdrop(elem) => elem.current_commit(),
|
2022-11-22 10:28:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn src(&self) -> Rectangle<f64, smithay::utils::Buffer> {
|
|
|
|
|
match self {
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.src(),
|
2023-09-15 18:37:34 +02:00
|
|
|
WorkspaceRenderElement::Fullscreen(elem) => elem.src(),
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.src(),
|
2022-11-22 10:28:30 +01:00
|
|
|
WorkspaceRenderElement::Window(elem) => elem.src(),
|
2023-05-19 19:44:57 +02:00
|
|
|
WorkspaceRenderElement::Backdrop(elem) => elem.src(),
|
2022-11-22 10:28:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, smithay::utils::Physical> {
|
|
|
|
|
match self {
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.geometry(scale),
|
2023-09-15 18:37:34 +02:00
|
|
|
WorkspaceRenderElement::Fullscreen(elem) => elem.geometry(scale),
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.geometry(scale),
|
2022-11-22 10:28:30 +01:00
|
|
|
WorkspaceRenderElement::Window(elem) => elem.geometry(scale),
|
2023-05-19 19:44:57 +02:00
|
|
|
WorkspaceRenderElement::Backdrop(elem) => elem.geometry(scale),
|
2022-11-22 10:28:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn location(&self, scale: Scale<f64>) -> Point<i32, smithay::utils::Physical> {
|
|
|
|
|
match self {
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.location(scale),
|
2023-09-15 18:37:34 +02:00
|
|
|
WorkspaceRenderElement::Fullscreen(elem) => elem.location(scale),
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.location(scale),
|
2022-11-22 10:28:30 +01:00
|
|
|
WorkspaceRenderElement::Window(elem) => elem.location(scale),
|
2023-05-19 19:44:57 +02:00
|
|
|
WorkspaceRenderElement::Backdrop(elem) => elem.location(scale),
|
2022-11-22 10:28:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn transform(&self) -> smithay::utils::Transform {
|
|
|
|
|
match self {
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.transform(),
|
2023-09-15 18:37:34 +02:00
|
|
|
WorkspaceRenderElement::Fullscreen(elem) => elem.transform(),
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.transform(),
|
2022-11-22 10:28:30 +01:00
|
|
|
WorkspaceRenderElement::Window(elem) => elem.transform(),
|
2023-05-19 19:44:57 +02:00
|
|
|
WorkspaceRenderElement::Backdrop(elem) => elem.transform(),
|
2022-11-22 10:28:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn damage_since(
|
|
|
|
|
&self,
|
|
|
|
|
scale: Scale<f64>,
|
|
|
|
|
commit: Option<smithay::backend::renderer::utils::CommitCounter>,
|
2024-04-08 16:55:41 -07:00
|
|
|
) -> DamageSet<i32, smithay::utils::Physical> {
|
2022-11-22 10:28:30 +01:00
|
|
|
match self {
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.damage_since(scale, commit),
|
2023-09-15 18:37:34 +02:00
|
|
|
WorkspaceRenderElement::Fullscreen(elem) => elem.damage_since(scale, commit),
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.damage_since(scale, commit),
|
2022-11-22 10:28:30 +01:00
|
|
|
WorkspaceRenderElement::Window(elem) => elem.damage_since(scale, commit),
|
2023-05-19 19:44:57 +02:00
|
|
|
WorkspaceRenderElement::Backdrop(elem) => elem.damage_since(scale, commit),
|
2022-11-22 10:28:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-13 14:16:21 -07:00
|
|
|
fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, smithay::utils::Physical> {
|
2022-11-22 10:28:30 +01:00
|
|
|
match self {
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.opaque_regions(scale),
|
2023-09-15 18:37:34 +02:00
|
|
|
WorkspaceRenderElement::Fullscreen(elem) => elem.opaque_regions(scale),
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.opaque_regions(scale),
|
2022-11-22 10:28:30 +01:00
|
|
|
WorkspaceRenderElement::Window(elem) => elem.opaque_regions(scale),
|
2023-05-19 19:44:57 +02:00
|
|
|
WorkspaceRenderElement::Backdrop(elem) => elem.opaque_regions(scale),
|
2022-11-22 10:28:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
2023-05-12 20:01:37 +02:00
|
|
|
|
|
|
|
|
fn alpha(&self) -> f32 {
|
|
|
|
|
match self {
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.alpha(),
|
2023-09-15 18:37:34 +02:00
|
|
|
WorkspaceRenderElement::Fullscreen(elem) => elem.alpha(),
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.alpha(),
|
2023-05-12 20:01:37 +02:00
|
|
|
WorkspaceRenderElement::Window(elem) => elem.alpha(),
|
2023-05-19 19:44:57 +02:00
|
|
|
WorkspaceRenderElement::Backdrop(elem) => elem.alpha(),
|
2023-05-12 20:01:37 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-11-22 10:28:30 +01:00
|
|
|
}
|
|
|
|
|
|
2024-08-04 18:04:06 -07:00
|
|
|
impl<R> RenderElement<R> for WorkspaceRenderElement<R>
|
|
|
|
|
where
|
|
|
|
|
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
2025-03-11 19:14:49 +01:00
|
|
|
R::TextureId: 'static,
|
|
|
|
|
R::Error: FromGlesError,
|
2024-08-04 18:04:06 -07:00
|
|
|
{
|
2024-08-30 13:58:49 +01:00
|
|
|
fn draw(
|
2022-11-22 10:28:30 +01:00
|
|
|
&self,
|
2025-03-11 19:14:49 +01:00
|
|
|
frame: &mut R::Frame<'_, '_>,
|
2022-12-27 18:27:29 +01:00
|
|
|
src: Rectangle<f64, BufferCoords>,
|
|
|
|
|
dst: Rectangle<i32, Physical>,
|
2022-11-22 10:28:30 +01:00
|
|
|
damage: &[Rectangle<i32, smithay::utils::Physical>],
|
2024-06-18 19:23:16 -07:00
|
|
|
opaque_regions: &[Rectangle<i32, Physical>],
|
2024-08-04 18:04:06 -07:00
|
|
|
) -> Result<(), R::Error> {
|
2022-11-22 10:28:30 +01:00
|
|
|
match self {
|
2024-06-18 19:23:16 -07:00
|
|
|
WorkspaceRenderElement::OverrideRedirect(elem) => {
|
|
|
|
|
elem.draw(frame, src, dst, damage, opaque_regions)
|
|
|
|
|
}
|
|
|
|
|
WorkspaceRenderElement::Fullscreen(elem) => {
|
|
|
|
|
elem.draw(frame, src, dst, damage, opaque_regions)
|
|
|
|
|
}
|
|
|
|
|
WorkspaceRenderElement::FullscreenPopup(elem) => {
|
|
|
|
|
elem.draw(frame, src, dst, damage, opaque_regions)
|
|
|
|
|
}
|
|
|
|
|
WorkspaceRenderElement::Window(elem) => {
|
|
|
|
|
elem.draw(frame, src, dst, damage, opaque_regions)
|
2023-05-19 19:44:57 +02:00
|
|
|
}
|
2024-06-18 19:23:16 -07:00
|
|
|
WorkspaceRenderElement::Backdrop(elem) => RenderElement::<GlowRenderer>::draw(
|
|
|
|
|
elem,
|
2024-08-04 18:04:06 -07:00
|
|
|
R::glow_frame_mut(frame),
|
2024-06-18 19:23:16 -07:00
|
|
|
src,
|
|
|
|
|
dst,
|
|
|
|
|
damage,
|
|
|
|
|
opaque_regions,
|
|
|
|
|
)
|
2024-08-04 18:04:06 -07:00
|
|
|
.map_err(FromGlesError::from_gles_error),
|
2022-11-22 10:28:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn underlying_storage(
|
|
|
|
|
&self,
|
2024-08-04 18:04:06 -07:00
|
|
|
renderer: &mut R,
|
2023-02-24 14:07:40 +01:00
|
|
|
) -> Option<smithay::backend::renderer::element::UnderlyingStorage> {
|
2022-11-22 10:28:30 +01:00
|
|
|
match self {
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.underlying_storage(renderer),
|
2023-09-15 18:37:34 +02:00
|
|
|
WorkspaceRenderElement::Fullscreen(elem) => elem.underlying_storage(renderer),
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.underlying_storage(renderer),
|
2022-11-22 10:28:30 +01:00
|
|
|
WorkspaceRenderElement::Window(elem) => elem.underlying_storage(renderer),
|
2023-05-19 19:44:57 +02:00
|
|
|
WorkspaceRenderElement::Backdrop(elem) => {
|
|
|
|
|
elem.underlying_storage(renderer.glow_renderer_mut())
|
|
|
|
|
}
|
2022-11-22 10:28:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-18 18:29:13 +02:00
|
|
|
impl<R> From<RescaleRenderElement<CosmicWindowRenderElement<R>>> for WorkspaceRenderElement<R>
|
2023-09-15 18:37:34 +02:00
|
|
|
where
|
|
|
|
|
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
2025-03-11 19:14:49 +01:00
|
|
|
R::TextureId: 'static,
|
2023-09-15 18:37:34 +02:00
|
|
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
|
|
|
|
{
|
2023-09-18 18:29:13 +02:00
|
|
|
fn from(elem: RescaleRenderElement<CosmicWindowRenderElement<R>>) -> Self {
|
2023-09-15 18:37:34 +02:00
|
|
|
WorkspaceRenderElement::Fullscreen(elem)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-18 18:29:13 +02:00
|
|
|
impl<R> From<CosmicWindowRenderElement<R>> for WorkspaceRenderElement<R>
|
|
|
|
|
where
|
|
|
|
|
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
2025-03-11 19:14:49 +01:00
|
|
|
R::TextureId: 'static,
|
2023-09-18 18:29:13 +02:00
|
|
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
|
|
|
|
{
|
|
|
|
|
fn from(elem: CosmicWindowRenderElement<R>) -> Self {
|
|
|
|
|
WorkspaceRenderElement::FullscreenPopup(elem)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-28 17:48:50 +01:00
|
|
|
impl<R> From<WaylandSurfaceRenderElement<R>> for WorkspaceRenderElement<R>
|
2022-11-22 10:28:30 +01:00
|
|
|
where
|
2023-01-16 15:12:25 +01:00
|
|
|
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
2025-03-11 19:14:49 +01:00
|
|
|
R::TextureId: 'static,
|
2022-11-22 10:28:30 +01:00
|
|
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
|
|
|
|
{
|
2022-11-28 17:48:50 +01:00
|
|
|
fn from(elem: WaylandSurfaceRenderElement<R>) -> Self {
|
2023-09-18 18:29:13 +02:00
|
|
|
WorkspaceRenderElement::OverrideRedirect(elem)
|
2022-11-22 10:28:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<R> From<CosmicMappedRenderElement<R>> for WorkspaceRenderElement<R>
|
|
|
|
|
where
|
2023-01-16 15:12:25 +01:00
|
|
|
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
2025-03-11 19:14:49 +01:00
|
|
|
R::TextureId: 'static,
|
2022-11-22 10:28:30 +01:00
|
|
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
|
|
|
|
{
|
|
|
|
|
fn from(elem: CosmicMappedRenderElement<R>) -> Self {
|
|
|
|
|
WorkspaceRenderElement::Window(elem)
|
|
|
|
|
}
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
2023-05-19 19:44:57 +02:00
|
|
|
|
|
|
|
|
impl<R> From<TextureRenderElement<GlesTexture>> for WorkspaceRenderElement<R>
|
|
|
|
|
where
|
|
|
|
|
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
2025-03-11 19:14:49 +01:00
|
|
|
R::TextureId: 'static,
|
2023-05-19 19:44:57 +02:00
|
|
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
|
|
|
|
{
|
|
|
|
|
fn from(elem: TextureRenderElement<GlesTexture>) -> Self {
|
|
|
|
|
WorkspaceRenderElement::Backdrop(elem)
|
|
|
|
|
}
|
|
|
|
|
}
|