2023-05-12 20:01:37 +02:00
|
|
|
use calloop::LoopHandle;
|
2023-10-25 19:40:26 +02:00
|
|
|
use indexmap::IndexMap;
|
2022-09-28 12:01:29 +02:00
|
|
|
use serde::{Deserialize, Serialize};
|
2023-06-22 21:30:45 +02:00
|
|
|
use std::{
|
|
|
|
|
collections::HashMap,
|
|
|
|
|
sync::atomic::{AtomicBool, Ordering},
|
2023-07-06 00:03:26 +02:00
|
|
|
time::{Duration, Instant},
|
2023-06-22 21:30:45 +02:00
|
|
|
};
|
2023-02-24 17:41:52 +01:00
|
|
|
use tracing::warn;
|
2023-07-05 23:48:10 +02:00
|
|
|
use wayland_backend::server::ClientId;
|
2022-07-04 15:28:03 +02:00
|
|
|
|
2022-09-28 12:01:29 +02:00
|
|
|
use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::State as WState;
|
2023-07-11 17:12:56 +02:00
|
|
|
use keyframe::{ease, functions::EaseInOutCubic};
|
2022-07-04 15:28:03 +02:00
|
|
|
use smithay::{
|
2023-01-23 22:56:42 +01:00
|
|
|
desktop::{
|
|
|
|
|
layer_map_for_output, space::SpaceElement, LayerSurface, PopupManager, WindowSurfaceType,
|
|
|
|
|
},
|
2023-01-18 20:23:41 +01:00
|
|
|
input::{
|
|
|
|
|
pointer::{Focus, GrabStartData as PointerGrabStartData},
|
|
|
|
|
Seat,
|
|
|
|
|
},
|
2022-09-09 20:00:00 -07:00
|
|
|
output::Output,
|
2023-07-05 23:48:10 +02:00
|
|
|
reexports::wayland_server::{protocol::wl_surface::WlSurface, Client, DisplayHandle},
|
2023-10-11 19:06:13 +02:00
|
|
|
utils::{Point, Rectangle, Serial, SERIAL_COUNTER},
|
2022-03-30 22:00:44 +02:00
|
|
|
wayland::{
|
2022-04-05 16:35:58 +02:00
|
|
|
compositor::with_states,
|
2023-01-18 20:23:41 +01:00
|
|
|
seat::WaylandFocus,
|
2022-04-21 14:02:44 +02:00
|
|
|
shell::{
|
2022-07-04 16:00:29 +02:00
|
|
|
wlr_layer::{
|
|
|
|
|
KeyboardInteractivity, Layer, LayerSurfaceCachedState, WlrLayerShellState,
|
|
|
|
|
},
|
2022-07-04 15:28:03 +02:00
|
|
|
xdg::XdgShellState,
|
2022-04-21 14:02:44 +02:00
|
|
|
},
|
2022-03-30 22:00:44 +02:00
|
|
|
},
|
2023-01-18 20:23:41 +01:00
|
|
|
xwayland::X11Surface,
|
2022-03-30 22:00:44 +02:00
|
|
|
};
|
2022-05-02 17:43:58 +02:00
|
|
|
|
2022-07-04 15:28:03 +02:00
|
|
|
use crate::{
|
2023-10-11 19:06:13 +02:00
|
|
|
config::{Config, KeyModifiers, KeyPattern, WorkspaceMode},
|
2023-08-29 17:00:11 -07:00
|
|
|
state::client_has_security_context,
|
2022-07-04 16:00:29 +02:00
|
|
|
utils::prelude::*,
|
2023-10-11 19:06:13 +02:00
|
|
|
wayland::protocols::{
|
|
|
|
|
toplevel_info::ToplevelInfoState,
|
|
|
|
|
toplevel_management::{ManagementCapabilities, ToplevelManagementState},
|
|
|
|
|
workspace::{
|
|
|
|
|
WorkspaceCapabilities, WorkspaceGroupHandle, WorkspaceHandle, WorkspaceState,
|
|
|
|
|
WorkspaceUpdateGuard,
|
2022-07-04 15:28:03 +02:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
};
|
2022-03-24 20:32:31 +01:00
|
|
|
|
2023-03-07 22:20:44 +01:00
|
|
|
pub mod element;
|
2022-07-04 16:00:29 +02:00
|
|
|
pub mod focus;
|
2022-10-27 20:40:55 +02:00
|
|
|
pub mod grabs;
|
2022-08-30 13:28:36 +02:00
|
|
|
pub mod layout;
|
2022-03-24 20:32:31 +01:00
|
|
|
mod workspace;
|
2023-01-16 20:31:43 +01:00
|
|
|
pub use self::element::{CosmicMapped, CosmicMappedRenderElement, CosmicSurface};
|
2022-03-24 20:32:31 +01:00
|
|
|
pub use self::workspace::*;
|
2022-09-28 12:01:29 +02:00
|
|
|
use self::{
|
2023-07-05 23:57:38 +02:00
|
|
|
element::{
|
|
|
|
|
resize_indicator::{resize_indicator, ResizeIndicator},
|
2023-08-11 18:15:22 +02:00
|
|
|
swap_indicator::{swap_indicator, SwapIndicator},
|
2023-07-05 23:57:38 +02:00
|
|
|
CosmicWindow,
|
|
|
|
|
},
|
2022-09-28 12:01:29 +02:00
|
|
|
focus::target::KeyboardFocusTarget,
|
2023-01-18 20:23:41 +01:00
|
|
|
grabs::ResizeEdge,
|
2023-10-11 19:06:13 +02:00
|
|
|
layout::{floating::ResizeState, tiling::NodeDesc},
|
2022-09-28 12:01:29 +02:00
|
|
|
};
|
2022-03-24 20:32:31 +01:00
|
|
|
|
2023-07-06 00:03:26 +02:00
|
|
|
const ANIMATION_DURATION: Duration = Duration::from_millis(200);
|
|
|
|
|
|
2023-07-17 21:11:45 +02:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub enum Trigger {
|
2023-08-11 18:15:22 +02:00
|
|
|
KeyboardSwap(KeyPattern, NodeDesc),
|
|
|
|
|
KeyboardMove(KeyModifiers),
|
2023-07-17 21:11:45 +02:00
|
|
|
Pointer(u32),
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-19 19:44:57 +02:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub enum OverviewMode {
|
|
|
|
|
None,
|
2023-07-17 21:11:45 +02:00
|
|
|
Started(Trigger, Instant),
|
2023-08-11 18:15:22 +02:00
|
|
|
Ended(Option<Trigger>, Instant),
|
2023-05-19 19:44:57 +02:00
|
|
|
}
|
|
|
|
|
|
2023-07-06 00:03:26 +02:00
|
|
|
impl OverviewMode {
|
|
|
|
|
pub fn alpha(&self) -> Option<f32> {
|
|
|
|
|
match self {
|
|
|
|
|
OverviewMode::Started(_, start) => {
|
2023-07-11 17:12:56 +02:00
|
|
|
let percentage = Instant::now().duration_since(*start).as_millis() as f32
|
|
|
|
|
/ ANIMATION_DURATION.as_millis() as f32;
|
|
|
|
|
Some(ease(EaseInOutCubic, 0.0, 1.0, percentage))
|
2023-07-06 00:03:26 +02:00
|
|
|
}
|
2023-08-11 18:15:22 +02:00
|
|
|
OverviewMode::Ended(_, end) => {
|
2023-07-11 17:12:56 +02:00
|
|
|
let percentage = Instant::now().duration_since(*end).as_millis() as f32
|
|
|
|
|
/ ANIMATION_DURATION.as_millis() as f32;
|
|
|
|
|
if percentage < 1.0 {
|
|
|
|
|
Some(ease(EaseInOutCubic, 1.0, 0.0, percentage))
|
2023-07-06 00:03:26 +02:00
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
OverviewMode::None => None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-28 19:20:06 +02:00
|
|
|
#[derive(Debug, Clone, Copy, serde::Deserialize, PartialEq, Eq, Hash)]
|
|
|
|
|
pub enum ResizeDirection {
|
|
|
|
|
Inwards,
|
|
|
|
|
Outwards,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub enum ResizeMode {
|
|
|
|
|
None,
|
|
|
|
|
Started(KeyPattern, Instant, ResizeDirection),
|
|
|
|
|
Ended(Instant, ResizeDirection),
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-06 00:03:26 +02:00
|
|
|
impl ResizeMode {
|
|
|
|
|
pub fn alpha(&self) -> Option<f32> {
|
|
|
|
|
match self {
|
|
|
|
|
ResizeMode::Started(_, start, _) => {
|
2023-07-11 17:12:56 +02:00
|
|
|
let percentage = Instant::now().duration_since(*start).as_millis() as f32
|
|
|
|
|
/ ANIMATION_DURATION.as_millis() as f32;
|
|
|
|
|
Some(ease(EaseInOutCubic, 0.0, 1.0, percentage))
|
2023-07-06 00:03:26 +02:00
|
|
|
}
|
|
|
|
|
ResizeMode::Ended(end, _) => {
|
2023-07-11 17:12:56 +02:00
|
|
|
let percentage = Instant::now().duration_since(*end).as_millis() as f32
|
|
|
|
|
/ ANIMATION_DURATION.as_millis() as f32;
|
|
|
|
|
if percentage < 1.0 {
|
|
|
|
|
Some(ease(EaseInOutCubic, 1.0, 0.0, percentage))
|
2023-07-06 00:03:26 +02:00
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ResizeMode::None => None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 19:40:26 +02:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
|
pub enum MaximizeMode {
|
|
|
|
|
Floating,
|
|
|
|
|
OnTop,
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-12 18:54:53 +02:00
|
|
|
#[derive(Debug)]
|
2022-07-04 15:28:03 +02:00
|
|
|
pub struct Shell {
|
2023-10-25 19:40:26 +02:00
|
|
|
pub workspaces: Workspaces,
|
|
|
|
|
|
2022-07-04 15:28:03 +02:00
|
|
|
pub popups: PopupManager,
|
2023-10-25 19:40:26 +02:00
|
|
|
pub maximize_mode: MaximizeMode,
|
2023-10-11 22:00:20 +02:00
|
|
|
pub pending_windows: Vec<(CosmicSurface, Seat<State>, Option<Output>)>,
|
2022-07-04 15:28:03 +02:00
|
|
|
pub pending_layers: Vec<(LayerSurface, Output, Seat<State>)>,
|
2023-01-27 19:51:23 +01:00
|
|
|
pub override_redirect_windows: Vec<X11Surface>,
|
2022-07-04 15:28:03 +02:00
|
|
|
|
|
|
|
|
// wayland_state
|
|
|
|
|
pub layer_shell_state: WlrLayerShellState,
|
2023-01-16 15:12:25 +01:00
|
|
|
pub toplevel_info_state: ToplevelInfoState<State, CosmicSurface>,
|
2022-07-18 21:26:02 +02:00
|
|
|
pub toplevel_management_state: ToplevelManagementState,
|
2022-07-04 15:28:03 +02:00
|
|
|
pub xdg_shell_state: XdgShellState,
|
|
|
|
|
pub workspace_state: WorkspaceState<State>,
|
2023-03-09 18:27:29 +01:00
|
|
|
|
2023-05-19 19:44:57 +02:00
|
|
|
overview_mode: OverviewMode,
|
2023-08-11 18:15:22 +02:00
|
|
|
swap_indicator: Option<SwapIndicator>,
|
2023-06-28 19:20:06 +02:00
|
|
|
resize_mode: ResizeMode,
|
2023-07-05 23:57:38 +02:00
|
|
|
resize_state: Option<(
|
|
|
|
|
KeyboardFocusTarget,
|
|
|
|
|
ResizeDirection,
|
|
|
|
|
ResizeEdge,
|
|
|
|
|
i32,
|
|
|
|
|
usize,
|
|
|
|
|
Output,
|
|
|
|
|
)>,
|
2023-07-06 00:03:26 +02:00
|
|
|
resize_indicator: Option<ResizeIndicator>,
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-28 12:01:29 +02:00
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct WorkspaceSet {
|
2023-05-22 20:19:11 +02:00
|
|
|
previously_active: Option<(usize, Instant)>,
|
2022-09-28 12:01:29 +02:00
|
|
|
active: usize,
|
|
|
|
|
group: WorkspaceGroupHandle,
|
2022-11-22 17:09:49 +01:00
|
|
|
idx: usize,
|
2023-01-27 13:26:28 +01:00
|
|
|
tiling_enabled: bool,
|
2023-03-09 18:27:29 +01:00
|
|
|
gaps: (u8, u8),
|
2023-10-25 19:40:26 +02:00
|
|
|
output: Output,
|
2022-11-22 17:09:49 +01:00
|
|
|
pub(crate) workspaces: Vec<Workspace>,
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
|
|
|
|
pub enum WorkspaceAmount {
|
|
|
|
|
Dynamic,
|
|
|
|
|
Static(u8),
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-28 12:01:29 +02:00
|
|
|
fn create_workspace(
|
|
|
|
|
state: &mut WorkspaceUpdateGuard<'_, State>,
|
2023-10-25 19:40:26 +02:00
|
|
|
output: &Output,
|
2022-09-28 12:01:29 +02:00
|
|
|
group_handle: &WorkspaceGroupHandle,
|
|
|
|
|
active: bool,
|
2023-01-27 13:26:28 +01:00
|
|
|
tiling: bool,
|
2023-03-09 18:27:29 +01:00
|
|
|
gaps: (u8, u8),
|
2022-09-28 12:01:29 +02:00
|
|
|
) -> Workspace {
|
|
|
|
|
let workspace_handle = state.create_workspace(&group_handle).unwrap();
|
|
|
|
|
if active {
|
|
|
|
|
state.add_workspace_state(&workspace_handle, WState::Active);
|
|
|
|
|
}
|
2022-11-08 09:38:43 +01:00
|
|
|
state.set_workspace_capabilities(
|
|
|
|
|
&workspace_handle,
|
|
|
|
|
[WorkspaceCapabilities::Activate].into_iter(),
|
|
|
|
|
);
|
2023-10-25 19:40:26 +02:00
|
|
|
Workspace::new(workspace_handle, output.clone(), tiling, gaps)
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl WorkspaceSet {
|
2022-11-22 17:09:49 +01:00
|
|
|
fn new(
|
|
|
|
|
state: &mut WorkspaceUpdateGuard<'_, State>,
|
2023-10-11 19:06:13 +02:00
|
|
|
group_handle: WorkspaceGroupHandle,
|
2023-10-25 19:40:26 +02:00
|
|
|
output: &Output,
|
2022-11-22 17:09:49 +01:00
|
|
|
amount: WorkspaceAmount,
|
|
|
|
|
idx: usize,
|
2023-01-27 13:26:28 +01:00
|
|
|
tiling_enabled: bool,
|
2023-03-09 18:27:29 +01:00
|
|
|
gaps: (u8, u8),
|
2022-11-22 17:09:49 +01:00
|
|
|
) -> WorkspaceSet {
|
2022-09-28 12:01:29 +02:00
|
|
|
let workspaces = match amount {
|
|
|
|
|
WorkspaceAmount::Dynamic => {
|
2023-10-25 19:40:26 +02:00
|
|
|
let workspace =
|
|
|
|
|
create_workspace(state, output, &group_handle, true, tiling_enabled, gaps);
|
2022-11-22 17:09:49 +01:00
|
|
|
workspace_set_idx(state, 1, idx, &workspace.handle);
|
2022-11-08 09:38:43 +01:00
|
|
|
state.set_workspace_capabilities(
|
|
|
|
|
&workspace.handle,
|
|
|
|
|
[WorkspaceCapabilities::Activate].into_iter(),
|
|
|
|
|
);
|
|
|
|
|
vec![workspace]
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
WorkspaceAmount::Static(len) => (0..len)
|
2022-11-08 09:38:43 +01:00
|
|
|
.map(|i| {
|
2023-10-25 19:40:26 +02:00
|
|
|
let workspace = create_workspace(
|
|
|
|
|
state,
|
|
|
|
|
output,
|
|
|
|
|
&group_handle,
|
|
|
|
|
i == 0,
|
|
|
|
|
tiling_enabled,
|
|
|
|
|
gaps,
|
|
|
|
|
);
|
2022-11-22 17:09:49 +01:00
|
|
|
workspace_set_idx(state, i + 1, idx, &workspace.handle);
|
2022-11-08 09:38:43 +01:00
|
|
|
state.set_workspace_capabilities(
|
|
|
|
|
&workspace.handle,
|
|
|
|
|
[WorkspaceCapabilities::Activate].into_iter(),
|
|
|
|
|
);
|
|
|
|
|
workspace
|
|
|
|
|
})
|
2022-09-28 12:01:29 +02:00
|
|
|
.collect(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
WorkspaceSet {
|
2023-05-22 20:19:11 +02:00
|
|
|
previously_active: None,
|
2022-09-28 12:01:29 +02:00
|
|
|
active: 0,
|
|
|
|
|
group: group_handle,
|
2022-11-22 17:09:49 +01:00
|
|
|
idx,
|
2023-01-27 13:26:28 +01:00
|
|
|
tiling_enabled,
|
2023-03-09 18:27:29 +01:00
|
|
|
gaps,
|
2022-09-28 12:01:29 +02:00
|
|
|
workspaces,
|
2023-10-25 19:40:26 +02:00
|
|
|
output: output.clone(),
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-25 00:10:24 +02:00
|
|
|
fn activate(
|
|
|
|
|
&mut self,
|
|
|
|
|
idx: usize,
|
|
|
|
|
state: &mut WorkspaceUpdateGuard<'_, State>,
|
|
|
|
|
) -> Result<bool, InvalidWorkspaceIndex> {
|
|
|
|
|
if idx >= self.workspaces.len() {
|
|
|
|
|
return Err(InvalidWorkspaceIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if self.active != idx {
|
2022-09-28 12:01:29 +02:00
|
|
|
let old_active = self.active;
|
|
|
|
|
state.remove_workspace_state(&self.workspaces[old_active].handle, WState::Active);
|
|
|
|
|
state.add_workspace_state(&self.workspaces[idx].handle, WState::Active);
|
2023-05-22 20:19:11 +02:00
|
|
|
self.previously_active = Some((old_active, Instant::now()));
|
2022-09-28 12:01:29 +02:00
|
|
|
self.active = idx;
|
2023-05-25 00:10:24 +02:00
|
|
|
Ok(true)
|
|
|
|
|
} else {
|
|
|
|
|
Ok(false)
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 19:40:26 +02:00
|
|
|
fn set_output(
|
2022-09-28 12:01:29 +02:00
|
|
|
&mut self,
|
2023-10-25 19:40:26 +02:00
|
|
|
new_output: &Output,
|
2023-01-16 15:12:25 +01:00
|
|
|
toplevel_info: &mut ToplevelInfoState<State, CosmicSurface>,
|
2022-09-28 12:01:29 +02:00
|
|
|
) {
|
2023-10-25 19:40:26 +02:00
|
|
|
for workspace in &mut self.workspaces {
|
|
|
|
|
workspace.set_output(new_output, toplevel_info);
|
|
|
|
|
}
|
|
|
|
|
self.output = new_output.clone();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn refresh<'a>(&mut self) {
|
2023-05-22 20:19:11 +02:00
|
|
|
if let Some((_, start)) = self.previously_active {
|
|
|
|
|
if Instant::now().duration_since(start).as_millis() >= ANIMATION_DURATION.as_millis() {
|
|
|
|
|
self.previously_active = None;
|
|
|
|
|
}
|
2023-09-14 15:30:08 +02:00
|
|
|
} else {
|
|
|
|
|
self.workspaces[self.active].refresh();
|
|
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2023-10-25 19:40:26 +02:00
|
|
|
fn add_empty_workspace(&mut self, state: &mut WorkspaceUpdateGuard<State>) {
|
2023-10-11 19:06:13 +02:00
|
|
|
let workspace = create_workspace(
|
2023-10-25 19:40:26 +02:00
|
|
|
state,
|
|
|
|
|
&self.output,
|
2023-10-25 19:41:30 +02:00
|
|
|
&self.group,
|
|
|
|
|
false,
|
|
|
|
|
self.tiling_enabled,
|
|
|
|
|
self.gaps,
|
|
|
|
|
);
|
|
|
|
|
workspace_set_idx(
|
2023-10-25 19:40:26 +02:00
|
|
|
state,
|
2023-10-25 19:41:30 +02:00
|
|
|
self.workspaces.len() as u8 + 1,
|
|
|
|
|
self.idx,
|
|
|
|
|
&workspace.handle,
|
|
|
|
|
);
|
|
|
|
|
self.workspaces.push(workspace);
|
|
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
|
2023-10-25 19:40:26 +02:00
|
|
|
fn ensure_last_empty<'a>(&mut self, state: &mut WorkspaceUpdateGuard<State>) {
|
|
|
|
|
// add empty at the end, if necessary
|
|
|
|
|
if self
|
|
|
|
|
.workspaces
|
|
|
|
|
.last()
|
|
|
|
|
.map(|last| last.windows().next().is_some())
|
|
|
|
|
.unwrap_or(true)
|
|
|
|
|
{
|
|
|
|
|
self.add_empty_workspace(state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// remove empty workspaces in between, if they are not active
|
2022-09-28 12:01:29 +02:00
|
|
|
let len = self.workspaces.len();
|
|
|
|
|
let mut keep = vec![true; len];
|
|
|
|
|
for (i, workspace) in self.workspaces.iter().enumerate() {
|
|
|
|
|
let has_windows = workspace.windows().next().is_some();
|
|
|
|
|
|
|
|
|
|
if !has_windows && i != self.active && i != len - 1 {
|
2022-11-08 09:38:43 +01:00
|
|
|
state.remove_workspace(workspace.handle);
|
2022-09-28 12:01:29 +02:00
|
|
|
keep[i] = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut iter = keep.iter();
|
|
|
|
|
self.workspaces.retain(|_| *iter.next().unwrap());
|
2022-10-25 17:52:18 +02:00
|
|
|
self.active -= keep
|
|
|
|
|
.iter()
|
|
|
|
|
.take(self.active + 1)
|
|
|
|
|
.filter(|keep| !**keep)
|
|
|
|
|
.count();
|
2022-11-08 09:38:43 +01:00
|
|
|
|
|
|
|
|
if keep.iter().any(|val| *val == false) {
|
|
|
|
|
for (i, workspace) in self.workspaces.iter().enumerate() {
|
2023-10-25 19:40:26 +02:00
|
|
|
workspace_set_idx(state, i as u8 + 1, self.idx, &workspace.handle);
|
2022-11-08 09:38:43 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2023-10-25 19:40:26 +02:00
|
|
|
fn ensure_static(
|
2022-09-28 12:01:29 +02:00
|
|
|
&mut self,
|
|
|
|
|
amount: usize,
|
2023-10-25 19:40:26 +02:00
|
|
|
state: &mut WorkspaceUpdateGuard<State>,
|
2023-01-16 15:12:25 +01:00
|
|
|
toplevel_info: &mut ToplevelInfoState<State, CosmicSurface>,
|
2022-09-28 12:01:29 +02:00
|
|
|
) {
|
|
|
|
|
if amount < self.workspaces.len() {
|
|
|
|
|
// merge last ones
|
|
|
|
|
let overflow = self.workspaces.split_off(amount);
|
|
|
|
|
if self.active >= self.workspaces.len() {
|
|
|
|
|
self.active = self.workspaces.len() - 1;
|
|
|
|
|
state.add_workspace_state(&self.workspaces[self.active].handle, WState::Active);
|
|
|
|
|
}
|
|
|
|
|
let last_space = self.workspaces.last_mut().unwrap();
|
|
|
|
|
|
2023-10-25 19:40:26 +02:00
|
|
|
for mut workspace in overflow {
|
|
|
|
|
if last_space.fullscreen.is_some() {
|
2023-10-11 22:00:20 +02:00
|
|
|
// Don't handle the returned original workspace, for this nieche case.
|
|
|
|
|
let _ = workspace.remove_fullscreen();
|
2023-10-25 19:40:26 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-28 12:01:29 +02:00
|
|
|
for element in workspace.mapped() {
|
|
|
|
|
// fixup toplevel state
|
|
|
|
|
for (toplevel, _) in element.windows() {
|
|
|
|
|
toplevel_info.toplevel_leave_workspace(&toplevel, &workspace.handle);
|
|
|
|
|
toplevel_info.toplevel_enter_workspace(&toplevel, &last_space.handle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
last_space.tiling_layer.merge(workspace.tiling_layer);
|
|
|
|
|
last_space.floating_layer.merge(workspace.floating_layer);
|
2023-10-25 19:40:26 +02:00
|
|
|
if workspace.fullscreen.is_some() {
|
|
|
|
|
last_space.fullscreen = workspace.fullscreen;
|
|
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
state.remove_workspace(workspace.handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
last_space.refresh();
|
|
|
|
|
} else if amount > self.workspaces.len() {
|
|
|
|
|
// add empty ones
|
|
|
|
|
while amount > self.workspaces.len() {
|
2023-10-25 19:40:26 +02:00
|
|
|
let workspace = create_workspace(
|
|
|
|
|
state,
|
|
|
|
|
&self.output,
|
2023-03-09 18:27:29 +01:00
|
|
|
&self.group,
|
|
|
|
|
false,
|
|
|
|
|
self.tiling_enabled,
|
|
|
|
|
self.gaps,
|
|
|
|
|
);
|
2022-11-08 09:38:43 +01:00
|
|
|
workspace_set_idx(
|
2023-10-25 19:40:26 +02:00
|
|
|
state,
|
2022-11-08 09:38:43 +01:00
|
|
|
self.workspaces.len() as u8 + 1,
|
2022-11-22 17:09:49 +01:00
|
|
|
self.idx,
|
2022-11-08 09:38:43 +01:00
|
|
|
&workspace.handle,
|
|
|
|
|
);
|
|
|
|
|
state.set_workspace_capabilities(
|
|
|
|
|
&workspace.handle,
|
|
|
|
|
[WorkspaceCapabilities::Activate].into_iter(),
|
|
|
|
|
);
|
2022-10-25 17:25:22 +02:00
|
|
|
self.workspaces.push(workspace);
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-22 17:09:49 +01:00
|
|
|
|
|
|
|
|
fn update_idx(&mut self, state: &mut WorkspaceUpdateGuard<'_, State>, idx: usize) {
|
|
|
|
|
self.idx = idx;
|
|
|
|
|
for (i, workspace) in self.workspaces.iter().enumerate() {
|
2022-12-07 09:47:07 +01:00
|
|
|
workspace_set_idx(state, i as u8 + 1, idx, &workspace.handle);
|
2022-11-22 17:09:49 +01:00
|
|
|
}
|
|
|
|
|
}
|
2023-01-27 13:26:28 +01:00
|
|
|
|
|
|
|
|
fn update_tiling_status(&mut self, seat: &Seat<State>, tiling_enabled: bool) {
|
|
|
|
|
self.tiling_enabled = tiling_enabled;
|
|
|
|
|
for workspace in &mut self.workspaces {
|
|
|
|
|
if workspace.tiling_enabled != tiling_enabled {
|
|
|
|
|
workspace.toggle_tiling(seat);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
2021-12-21 18:57:09 +01:00
|
|
|
|
2022-09-28 12:01:29 +02:00
|
|
|
#[derive(Debug)]
|
2023-10-25 19:40:26 +02:00
|
|
|
pub struct Workspaces {
|
|
|
|
|
sets: IndexMap<Output, WorkspaceSet>,
|
|
|
|
|
backup_set: Option<WorkspaceSet>,
|
2023-10-25 19:41:30 +02:00
|
|
|
amount: WorkspaceAmount,
|
2023-10-25 19:40:26 +02:00
|
|
|
mode: WorkspaceMode,
|
2023-10-25 19:41:30 +02:00
|
|
|
tiling_enabled: bool,
|
|
|
|
|
gaps: (u8, u8),
|
2023-10-25 19:40:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Workspaces {
|
|
|
|
|
pub fn new(config: &Config) -> Workspaces {
|
|
|
|
|
Workspaces {
|
|
|
|
|
sets: IndexMap::new(),
|
|
|
|
|
backup_set: None,
|
|
|
|
|
amount: config.static_conf.workspace_amount,
|
|
|
|
|
mode: config.static_conf.workspace_mode,
|
|
|
|
|
tiling_enabled: config.static_conf.tiling_enabled,
|
|
|
|
|
gaps: config.static_conf.gaps,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn add_output(
|
|
|
|
|
&mut self,
|
|
|
|
|
output: &Output,
|
|
|
|
|
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
|
|
|
|
|
toplevel_info_state: &mut ToplevelInfoState<State, CosmicSurface>,
|
|
|
|
|
) {
|
|
|
|
|
if self.sets.contains_key(output) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-11 19:06:13 +02:00
|
|
|
let set = self
|
|
|
|
|
.backup_set
|
|
|
|
|
.take()
|
|
|
|
|
.map(|mut set| {
|
|
|
|
|
set.set_output(output, toplevel_info_state);
|
|
|
|
|
set
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
|
let group_handle = match self.mode {
|
|
|
|
|
WorkspaceMode::Global => self
|
|
|
|
|
.sets
|
|
|
|
|
.first()
|
|
|
|
|
.map(|(_, w)| w.group.clone())
|
|
|
|
|
.unwrap_or(workspace_state.create_workspace_group()),
|
|
|
|
|
WorkspaceMode::OutputBound => workspace_state.create_workspace_group(),
|
|
|
|
|
};
|
|
|
|
|
WorkspaceSet::new(
|
|
|
|
|
workspace_state,
|
|
|
|
|
group_handle,
|
|
|
|
|
&output,
|
|
|
|
|
self.amount,
|
|
|
|
|
self.sets.len(),
|
|
|
|
|
self.tiling_enabled,
|
|
|
|
|
self.gaps,
|
|
|
|
|
)
|
|
|
|
|
});
|
2023-10-25 19:40:26 +02:00
|
|
|
workspace_state.add_group_output(&set.group, &output);
|
|
|
|
|
|
|
|
|
|
self.sets.insert(output.clone(), set);
|
|
|
|
|
for workspace in &mut self.sets.get_mut(output).unwrap().workspaces {
|
|
|
|
|
workspace.set_output(output, toplevel_info_state);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn remove_output(
|
|
|
|
|
&mut self,
|
|
|
|
|
output: &Output,
|
|
|
|
|
seats: impl Iterator<Item = Seat<State>>,
|
|
|
|
|
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
|
|
|
|
|
toplevel_info_state: &mut ToplevelInfoState<State, CosmicSurface>,
|
|
|
|
|
) {
|
|
|
|
|
if !self.sets.contains_key(output) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(set) = self.sets.remove(output) {
|
|
|
|
|
{
|
|
|
|
|
let map = layer_map_for_output(output);
|
|
|
|
|
for surface in map.layers() {
|
|
|
|
|
surface.layer_surface().send_close();
|
|
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
2023-10-25 19:40:26 +02:00
|
|
|
|
|
|
|
|
// TODO: Heuristic which output to move to.
|
|
|
|
|
// It is supposed to be the *most* internal, we just pick the first one for now
|
|
|
|
|
// and hope enumeration order works in our favor.
|
|
|
|
|
let new_output = self.sets.get_index(0).map(|(o, _)| o.clone());
|
|
|
|
|
if let Some(new_output) = new_output {
|
|
|
|
|
for seat in seats {
|
|
|
|
|
if &seat.active_output() == output {
|
|
|
|
|
seat.set_active_output(&new_output);
|
|
|
|
|
}
|
2023-10-25 19:41:30 +02:00
|
|
|
}
|
2023-10-25 19:40:26 +02:00
|
|
|
|
|
|
|
|
let new_set = self.sets.get_mut(&new_output).unwrap();
|
|
|
|
|
let workspace_group = new_set.group;
|
|
|
|
|
for mut workspace in set.workspaces {
|
|
|
|
|
// update workspace protocol state
|
|
|
|
|
workspace_state.remove_workspace(workspace.handle);
|
|
|
|
|
let workspace_handle =
|
|
|
|
|
workspace_state.create_workspace(&workspace_group).unwrap();
|
|
|
|
|
workspace_state.set_workspace_capabilities(
|
|
|
|
|
&workspace_handle,
|
|
|
|
|
[WorkspaceCapabilities::Activate].into_iter(),
|
|
|
|
|
);
|
|
|
|
|
let old_workspace_handle = workspace.handle;
|
|
|
|
|
workspace.handle = workspace_handle;
|
|
|
|
|
|
|
|
|
|
for window in workspace.mapped() {
|
|
|
|
|
for (surface, _) in window.windows() {
|
|
|
|
|
toplevel_info_state
|
|
|
|
|
.toplevel_leave_workspace(&surface, &old_workspace_handle);
|
|
|
|
|
toplevel_info_state
|
|
|
|
|
.toplevel_enter_workspace(&surface, &workspace.handle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// update mapping
|
|
|
|
|
workspace.set_output(&new_output, toplevel_info_state);
|
|
|
|
|
workspace.refresh();
|
|
|
|
|
|
|
|
|
|
// TODO: merge if mode = static
|
|
|
|
|
new_set.workspaces.push(workspace);
|
|
|
|
|
}
|
2023-10-11 19:06:13 +02:00
|
|
|
if self.mode == WorkspaceMode::OutputBound {
|
|
|
|
|
workspace_state.remove_workspace_group(set.group);
|
|
|
|
|
} else {
|
|
|
|
|
workspace_state.remove_group_output(&workspace_group, output);
|
|
|
|
|
}
|
2023-10-25 19:40:26 +02:00
|
|
|
|
|
|
|
|
for (i, set) in self.sets.values_mut().enumerate() {
|
|
|
|
|
set.update_idx(workspace_state, i);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
workspace_state.remove_group_output(&set.group, output);
|
|
|
|
|
self.backup_set = Some(set);
|
2022-11-10 17:22:16 +01:00
|
|
|
}
|
2023-10-11 19:06:13 +02:00
|
|
|
|
|
|
|
|
self.refresh(workspace_state, toplevel_info_state)
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-11 19:06:13 +02:00
|
|
|
pub fn update_config(
|
|
|
|
|
&mut self,
|
|
|
|
|
config: &Config,
|
|
|
|
|
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
|
|
|
|
|
toplevel_info_state: &mut ToplevelInfoState<State, CosmicSurface>,
|
|
|
|
|
) {
|
|
|
|
|
if self.sets.len() <= 1 {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match (self.mode, config.static_conf.workspace_mode) {
|
|
|
|
|
(WorkspaceMode::Global, WorkspaceMode::OutputBound) => {
|
|
|
|
|
// we need to create new separate workspace groups
|
|
|
|
|
let old_group = self.sets.first().map(|(_, w)| w.group.clone()).unwrap();
|
|
|
|
|
for (i, set) in self.sets.values_mut().enumerate() {
|
|
|
|
|
let new_group = workspace_state.create_workspace_group();
|
|
|
|
|
workspace_state.add_group_output(&new_group, &set.output);
|
|
|
|
|
|
|
|
|
|
for workspace in &mut set.workspaces {
|
|
|
|
|
// TODO: new protocol version allows workspaces to be reassigned,
|
|
|
|
|
// that would also avoid updating toplevel-info
|
|
|
|
|
let old_workspace_handle = workspace.handle;
|
|
|
|
|
workspace_state.remove_workspace(old_workspace_handle);
|
|
|
|
|
workspace.handle = workspace_state.create_workspace(&new_group).unwrap();
|
|
|
|
|
workspace_state.set_workspace_capabilities(
|
|
|
|
|
&workspace.handle,
|
|
|
|
|
[WorkspaceCapabilities::Activate].into_iter(),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
for window in workspace.mapped() {
|
|
|
|
|
for (surface, _) in window.windows() {
|
|
|
|
|
toplevel_info_state
|
|
|
|
|
.toplevel_leave_workspace(&surface, &old_workspace_handle);
|
|
|
|
|
toplevel_info_state
|
|
|
|
|
.toplevel_enter_workspace(&surface, &workspace.handle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set.update_idx(workspace_state, i);
|
|
|
|
|
workspace_state.remove_workspace_group(old_group);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
(WorkspaceMode::OutputBound, WorkspaceMode::Global) => {
|
|
|
|
|
// lets construct an iterator of all the pairs of workspaces we have to "merge"
|
|
|
|
|
let mut pairs = Vec::new();
|
|
|
|
|
if let Some(max) = self.sets.values().map(|set| set.workspaces.len()).max() {
|
|
|
|
|
let offset = self.sets.values().map(|set| set.active).max().unwrap();
|
|
|
|
|
for i in 0..max {
|
|
|
|
|
pairs.push(
|
|
|
|
|
self.sets
|
|
|
|
|
.values()
|
|
|
|
|
.map(|set| {
|
|
|
|
|
let idx = set.active as isize + i as isize - offset as isize;
|
|
|
|
|
if idx < 0 || idx >= set.workspaces.len() as isize {
|
|
|
|
|
None
|
|
|
|
|
} else {
|
|
|
|
|
Some(idx)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.collect::<Vec<_>>(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let group = workspace_state.create_workspace_group();
|
|
|
|
|
for output in self.sets.keys() {
|
|
|
|
|
workspace_state.add_group_output(&group, output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let old_workspace_groups = self.sets.values().map(|w| w.group).collect::<Vec<_>>();
|
|
|
|
|
for (j, pair) in pairs.iter().enumerate() {
|
|
|
|
|
for (i, x) in pair.iter().enumerate() {
|
|
|
|
|
// Fill up sets, where necessary
|
|
|
|
|
if x.is_none() {
|
|
|
|
|
// create missing workspace
|
|
|
|
|
let (output, set) = self.sets.get_index_mut(i).unwrap();
|
|
|
|
|
set.workspaces.insert(
|
|
|
|
|
j,
|
|
|
|
|
create_workspace(
|
|
|
|
|
workspace_state,
|
|
|
|
|
output,
|
|
|
|
|
&group,
|
|
|
|
|
false,
|
|
|
|
|
config.static_conf.tiling_enabled,
|
|
|
|
|
config.static_conf.gaps,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
// Otherwise just update
|
|
|
|
|
} else {
|
|
|
|
|
let set = &mut self.sets[i];
|
|
|
|
|
let workspace = &mut set.workspaces[j];
|
|
|
|
|
let old_workspace_handle = workspace.handle;
|
|
|
|
|
workspace_state.remove_workspace(old_workspace_handle);
|
|
|
|
|
workspace.handle = workspace_state.create_workspace(&group).unwrap();
|
|
|
|
|
workspace_state.set_workspace_capabilities(
|
|
|
|
|
&workspace.handle,
|
|
|
|
|
[WorkspaceCapabilities::Activate].into_iter(),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
for window in workspace.mapped() {
|
|
|
|
|
for (surface, _) in window.windows() {
|
|
|
|
|
toplevel_info_state
|
|
|
|
|
.toplevel_leave_workspace(&surface, &old_workspace_handle);
|
|
|
|
|
toplevel_info_state
|
|
|
|
|
.toplevel_enter_workspace(&surface, &workspace.handle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (i, set) in self.sets.values_mut().enumerate() {
|
|
|
|
|
set.group = group.clone();
|
|
|
|
|
set.update_idx(workspace_state, i);
|
|
|
|
|
}
|
|
|
|
|
for old_group in old_workspace_groups {
|
|
|
|
|
workspace_state.remove_workspace_group(old_group);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
self.refresh(workspace_state, toplevel_info_state)
|
|
|
|
|
}
|
2023-10-25 19:40:26 +02:00
|
|
|
|
|
|
|
|
pub fn refresh(
|
|
|
|
|
&mut self,
|
|
|
|
|
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
|
|
|
|
|
toplevel_info_state: &mut ToplevelInfoState<State, CosmicSurface>,
|
|
|
|
|
) {
|
|
|
|
|
match self.mode {
|
|
|
|
|
WorkspaceMode::Global => {
|
|
|
|
|
match self.amount {
|
|
|
|
|
WorkspaceAmount::Dynamic => {
|
|
|
|
|
// this should never happen
|
|
|
|
|
let max = self
|
|
|
|
|
.sets
|
|
|
|
|
.values()
|
|
|
|
|
.map(|set| set.workspaces.len())
|
|
|
|
|
.max()
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
for set in self
|
|
|
|
|
.sets
|
|
|
|
|
.values_mut()
|
|
|
|
|
.filter(|set| set.workspaces.len() < max)
|
|
|
|
|
{
|
|
|
|
|
while set.workspaces.len() < max {
|
|
|
|
|
set.add_empty_workspace(workspace_state)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// add empty at the end, if necessary
|
|
|
|
|
if self
|
|
|
|
|
.sets
|
|
|
|
|
.values()
|
|
|
|
|
.flat_map(|set| set.workspaces.last())
|
|
|
|
|
.any(|w| w.mapped().next().is_some())
|
|
|
|
|
{
|
|
|
|
|
for set in self.sets.values_mut() {
|
|
|
|
|
set.add_empty_workspace(workspace_state);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// remove empty workspaces in between, if they are not active
|
|
|
|
|
let len = self.sets[0].workspaces.len();
|
|
|
|
|
let mut active = self.sets[0].active;
|
|
|
|
|
let mut keep = vec![true; len];
|
|
|
|
|
for i in 0..len {
|
|
|
|
|
let has_windows = self
|
|
|
|
|
.sets
|
|
|
|
|
.values()
|
|
|
|
|
.any(|s| s.workspaces[i].windows().next().is_some());
|
|
|
|
|
|
|
|
|
|
if !has_windows && i != active && i != len - 1 {
|
|
|
|
|
for workspace in self.sets.values().map(|s| &s.workspaces[i]) {
|
|
|
|
|
workspace_state.remove_workspace(workspace.handle);
|
|
|
|
|
}
|
|
|
|
|
keep[i] = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.sets.values_mut().for_each(|s| {
|
|
|
|
|
let mut iter = keep.iter();
|
|
|
|
|
s.workspaces.retain(|_| *iter.next().unwrap());
|
|
|
|
|
});
|
|
|
|
|
active -= keep.iter().take(active + 1).filter(|keep| !**keep).count();
|
|
|
|
|
self.sets.values_mut().for_each(|s| {
|
|
|
|
|
s.active = active;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if keep.iter().any(|val| *val == false) {
|
|
|
|
|
for set in self.sets.values_mut() {
|
|
|
|
|
for (i, workspace) in set.workspaces.iter().enumerate() {
|
|
|
|
|
workspace_set_idx(
|
|
|
|
|
workspace_state,
|
|
|
|
|
i as u8 + 1,
|
|
|
|
|
set.idx,
|
|
|
|
|
&workspace.handle,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
WorkspaceAmount::Static(amount) => {
|
|
|
|
|
for set in self.sets.values_mut() {
|
|
|
|
|
set.ensure_static(amount as usize, workspace_state, toplevel_info_state)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
2023-10-25 19:40:26 +02:00
|
|
|
WorkspaceMode::OutputBound => match self.amount {
|
|
|
|
|
WorkspaceAmount::Dynamic => {
|
|
|
|
|
for set in self.sets.values_mut() {
|
|
|
|
|
set.ensure_last_empty(workspace_state);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
WorkspaceAmount::Static(amount) => {
|
|
|
|
|
for set in self.sets.values_mut() {
|
|
|
|
|
set.ensure_static(amount as usize, workspace_state, toplevel_info_state)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for set in self.sets.values_mut() {
|
|
|
|
|
set.refresh()
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 19:40:26 +02:00
|
|
|
pub fn get(&self, num: usize, output: &Output) -> Option<&Workspace> {
|
|
|
|
|
self.sets
|
|
|
|
|
.get(output)
|
|
|
|
|
.and_then(|set| set.workspaces.get(num))
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-28 12:01:29 +02:00
|
|
|
pub fn get_mut(&mut self, num: usize, output: &Output) -> Option<&mut Workspace> {
|
2023-10-25 19:40:26 +02:00
|
|
|
self.sets
|
2023-10-25 19:41:30 +02:00
|
|
|
.get_mut(output)
|
2023-10-25 19:40:26 +02:00
|
|
|
.and_then(|set| set.workspaces.get_mut(num))
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-22 20:19:11 +02:00
|
|
|
pub fn active(&self, output: &Output) -> (Option<(&Workspace, Instant)>, &Workspace) {
|
2023-10-25 19:40:26 +02:00
|
|
|
let set = self.sets.get(output).unwrap();
|
2023-10-25 19:41:30 +02:00
|
|
|
(
|
|
|
|
|
set.previously_active
|
|
|
|
|
.map(|(idx, start)| (&set.workspaces[idx], start)),
|
|
|
|
|
&set.workspaces[set.active],
|
|
|
|
|
)
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn active_mut(&mut self, output: &Output) -> &mut Workspace {
|
2023-10-25 19:40:26 +02:00
|
|
|
let set = self.sets.get_mut(output).unwrap();
|
2023-10-25 19:41:30 +02:00
|
|
|
&mut set.workspaces[set.active]
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-22 20:19:11 +02:00
|
|
|
pub fn active_num(&self, output: &Output) -> (Option<usize>, usize) {
|
2023-10-25 19:40:26 +02:00
|
|
|
let set = self.sets.get(output).unwrap();
|
2023-10-25 19:41:30 +02:00
|
|
|
(set.previously_active.map(|(idx, _)| idx), set.active)
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2022-11-10 18:42:11 +01:00
|
|
|
pub fn len(&self, output: &Output) -> usize {
|
2023-10-25 19:40:26 +02:00
|
|
|
let set = self.sets.get(output).unwrap();
|
2023-10-25 19:41:30 +02:00
|
|
|
set.workspaces.len()
|
|
|
|
|
}
|
2023-10-25 19:40:26 +02:00
|
|
|
|
|
|
|
|
pub fn iter(&self) -> impl Iterator<Item = (&Output, &WorkspaceSet)> {
|
|
|
|
|
self.sets.iter()
|
2022-11-10 18:42:11 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-28 12:01:29 +02:00
|
|
|
pub fn spaces(&self) -> impl Iterator<Item = &Workspace> {
|
2023-10-25 19:40:26 +02:00
|
|
|
self.sets.values().flat_map(|set| set.workspaces.iter())
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn spaces_for_output(&self, output: &Output) -> impl Iterator<Item = &Workspace> {
|
2023-10-25 19:40:26 +02:00
|
|
|
self.sets
|
|
|
|
|
.get(output)
|
2023-10-25 19:41:30 +02:00
|
|
|
.into_iter()
|
2023-10-25 19:40:26 +02:00
|
|
|
.flat_map(|set| set.workspaces.iter())
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn spaces_mut(&mut self) -> impl Iterator<Item = &mut Workspace> {
|
2023-10-25 19:40:26 +02:00
|
|
|
Box::new(
|
|
|
|
|
self.sets
|
|
|
|
|
.values_mut()
|
|
|
|
|
.flat_map(|set| set.workspaces.iter_mut()),
|
|
|
|
|
)
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
2023-01-27 13:26:28 +01:00
|
|
|
|
|
|
|
|
pub fn update_tiling_status(&mut self, seat: &Seat<State>, tiling: bool) {
|
2023-10-25 19:40:26 +02:00
|
|
|
for set in self.sets.values_mut() {
|
2023-10-25 19:41:30 +02:00
|
|
|
set.update_tiling_status(seat, tiling)
|
2023-01-27 13:26:28 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
2022-03-24 20:32:31 +01:00
|
|
|
|
2023-05-25 00:10:24 +02:00
|
|
|
pub struct InvalidWorkspaceIndex;
|
|
|
|
|
|
2022-03-24 20:32:31 +01:00
|
|
|
impl Shell {
|
2022-07-04 15:28:03 +02:00
|
|
|
pub fn new(config: &Config, dh: &DisplayHandle) -> Self {
|
2022-07-18 21:26:02 +02:00
|
|
|
// TODO: Privileged protocols
|
2023-02-24 17:41:52 +01:00
|
|
|
let layer_shell_state = WlrLayerShellState::new::<State>(dh);
|
|
|
|
|
let xdg_shell_state = XdgShellState::new::<State>(dh);
|
2022-07-18 21:26:02 +02:00
|
|
|
let toplevel_info_state = ToplevelInfoState::new(
|
|
|
|
|
dh,
|
2023-01-24 19:32:57 +01:00
|
|
|
//|client| client.get_data::<ClientState>().map_or(false, |s| s.privileged),
|
2023-08-29 17:00:11 -07:00
|
|
|
client_has_security_context,
|
2022-08-30 13:28:36 +02:00
|
|
|
);
|
2022-07-18 21:26:02 +02:00
|
|
|
let toplevel_management_state = ToplevelManagementState::new::<State, _>(
|
|
|
|
|
dh,
|
2022-08-30 13:28:36 +02:00
|
|
|
vec![
|
|
|
|
|
ManagementCapabilities::Close,
|
|
|
|
|
ManagementCapabilities::Activate,
|
|
|
|
|
],
|
2023-01-24 19:32:57 +01:00
|
|
|
//|client| client.get_data::<ClientState>().map_or(false, |s| s.privileged),
|
2023-08-29 17:00:11 -07:00
|
|
|
client_has_security_context,
|
2022-07-18 21:26:02 +02:00
|
|
|
);
|
2023-10-25 19:40:26 +02:00
|
|
|
let workspace_state = WorkspaceState::new(
|
2022-07-18 21:26:02 +02:00
|
|
|
dh,
|
2023-01-24 19:32:57 +01:00
|
|
|
//|client| client.get_data::<ClientState>().map_or(false, |s| s.privileged),
|
2023-08-29 17:00:11 -07:00
|
|
|
client_has_security_context,
|
2022-07-18 21:26:02 +02:00
|
|
|
);
|
2022-07-04 15:28:03 +02:00
|
|
|
|
2022-03-24 20:32:31 +01:00
|
|
|
Shell {
|
2023-02-24 17:41:52 +01:00
|
|
|
popups: PopupManager::default(),
|
2023-10-25 19:40:26 +02:00
|
|
|
workspaces: Workspaces::new(config),
|
|
|
|
|
maximize_mode: MaximizeMode::Floating,
|
2021-12-28 16:23:12 +01:00
|
|
|
|
2022-07-04 15:28:03 +02:00
|
|
|
pending_windows: Vec::new(),
|
|
|
|
|
pending_layers: Vec::new(),
|
2023-01-18 20:23:41 +01:00
|
|
|
override_redirect_windows: Vec::new(),
|
2022-04-05 16:35:58 +02:00
|
|
|
|
2022-07-04 15:28:03 +02:00
|
|
|
layer_shell_state,
|
|
|
|
|
toplevel_info_state,
|
2022-07-18 21:26:02 +02:00
|
|
|
toplevel_management_state,
|
2022-07-04 15:28:03 +02:00
|
|
|
xdg_shell_state,
|
|
|
|
|
workspace_state,
|
2023-03-09 18:27:29 +01:00
|
|
|
|
2023-05-19 19:44:57 +02:00
|
|
|
overview_mode: OverviewMode::None,
|
2023-08-11 18:15:22 +02:00
|
|
|
swap_indicator: None,
|
2023-06-28 19:20:06 +02:00
|
|
|
resize_mode: ResizeMode::None,
|
2023-07-05 23:57:38 +02:00
|
|
|
resize_state: None,
|
2023-07-06 00:03:26 +02:00
|
|
|
resize_indicator: None,
|
2022-07-04 15:28:03 +02:00
|
|
|
}
|
2022-04-05 16:35:58 +02:00
|
|
|
}
|
|
|
|
|
|
2022-04-14 22:16:37 +02:00
|
|
|
pub fn add_output(&mut self, output: &Output) {
|
2023-10-25 19:40:26 +02:00
|
|
|
self.workspaces.add_output(
|
2023-10-25 19:41:30 +02:00
|
|
|
output,
|
2023-10-25 19:40:26 +02:00
|
|
|
&mut self.workspace_state.update(),
|
|
|
|
|
&mut self.toplevel_info_state,
|
|
|
|
|
);
|
2022-04-13 22:59:14 +02:00
|
|
|
}
|
|
|
|
|
|
2022-11-22 10:07:17 +01:00
|
|
|
pub fn remove_output(&mut self, output: &Output, seats: impl Iterator<Item = Seat<State>>) {
|
2023-10-25 19:40:26 +02:00
|
|
|
self.workspaces.remove_output(
|
|
|
|
|
output,
|
|
|
|
|
seats,
|
|
|
|
|
&mut self.workspace_state.update(),
|
|
|
|
|
&mut self.toplevel_info_state,
|
|
|
|
|
);
|
2023-10-25 19:41:30 +02:00
|
|
|
self.refresh(); // cleans up excess of workspaces and empty workspaces
|
|
|
|
|
}
|
2022-03-24 20:32:31 +01:00
|
|
|
|
2023-10-11 19:06:13 +02:00
|
|
|
pub fn update_config(&mut self, config: &Config) {
|
|
|
|
|
let mut workspace_state = self.workspace_state.update();
|
|
|
|
|
let toplevel_info_state = &mut self.toplevel_info_state;
|
|
|
|
|
self.workspaces
|
|
|
|
|
.update_config(config, &mut workspace_state, toplevel_info_state);
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-25 00:10:24 +02:00
|
|
|
pub fn activate(
|
|
|
|
|
&mut self,
|
|
|
|
|
output: &Output,
|
|
|
|
|
idx: usize,
|
2023-10-25 19:40:26 +02:00
|
|
|
) -> Result<Option<Point<i32, Global>>, InvalidWorkspaceIndex> {
|
|
|
|
|
match &mut self.workspaces.mode {
|
|
|
|
|
WorkspaceMode::OutputBound => {
|
|
|
|
|
if let Some(set) = self.workspaces.sets.get_mut(output) {
|
2023-07-31 17:25:09 +02:00
|
|
|
if matches!(
|
|
|
|
|
self.overview_mode,
|
|
|
|
|
OverviewMode::Started(Trigger::Pointer(_), _)
|
|
|
|
|
) {
|
2023-10-25 19:40:26 +02:00
|
|
|
set.workspaces[set.active].tiling_layer.cleanup_drag();
|
2023-07-31 17:25:09 +02:00
|
|
|
}
|
2023-10-25 19:40:26 +02:00
|
|
|
set.activate(idx, &mut self.workspace_state.update())?;
|
|
|
|
|
|
2023-10-25 19:41:30 +02:00
|
|
|
let output_geo = output.geometry();
|
|
|
|
|
Ok(Some(
|
2023-10-25 19:40:26 +02:00
|
|
|
output_geo.loc
|
|
|
|
|
+ Point::from((output_geo.size.w / 2, output_geo.size.h / 2)),
|
2023-10-25 19:41:30 +02:00
|
|
|
))
|
|
|
|
|
} else {
|
|
|
|
|
Ok(None)
|
2023-10-25 19:40:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
WorkspaceMode::Global => {
|
|
|
|
|
for set in self.workspaces.sets.values_mut() {
|
|
|
|
|
set.activate(idx, &mut self.workspace_state.update())?;
|
|
|
|
|
}
|
|
|
|
|
Ok(None)
|
|
|
|
|
}
|
2022-07-04 15:28:03 +02:00
|
|
|
}
|
2021-12-28 16:23:12 +01:00
|
|
|
}
|
|
|
|
|
|
2022-03-24 20:32:31 +01:00
|
|
|
pub fn active_space(&self, output: &Output) -> &Workspace {
|
2023-07-21 16:08:55 +02:00
|
|
|
self.workspaces.active(output).1
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
2021-12-21 18:57:09 +01:00
|
|
|
|
2022-03-24 20:32:31 +01:00
|
|
|
pub fn active_space_mut(&mut self, output: &Output) -> &mut Workspace {
|
2023-07-21 16:08:55 +02:00
|
|
|
self.workspaces.active_mut(output)
|
2021-12-21 18:57:09 +01:00
|
|
|
}
|
2021-12-22 20:14:36 +01:00
|
|
|
|
2022-11-03 18:51:27 +01:00
|
|
|
pub fn visible_outputs_for_surface<'a>(
|
2022-08-30 13:28:36 +02:00
|
|
|
&'a self,
|
|
|
|
|
surface: &'a WlSurface,
|
|
|
|
|
) -> impl Iterator<Item = Output> + 'a {
|
2023-06-22 21:30:45 +02:00
|
|
|
match self
|
2023-10-25 19:40:26 +02:00
|
|
|
.outputs()
|
2023-06-22 21:30:45 +02:00
|
|
|
.find(|o| {
|
|
|
|
|
let map = layer_map_for_output(o);
|
|
|
|
|
map.layer_for_surface(surface, WindowSurfaceType::ALL)
|
|
|
|
|
.is_some()
|
|
|
|
|
})
|
|
|
|
|
.or_else(|| {
|
|
|
|
|
self.pending_layers.iter().find_map(|(l, output, _)| {
|
|
|
|
|
let found = AtomicBool::new(false);
|
|
|
|
|
l.with_surfaces(|s, _| {
|
|
|
|
|
found.fetch_or(s == surface, Ordering::SeqCst);
|
|
|
|
|
});
|
|
|
|
|
found.load(Ordering::SeqCst).then_some(output)
|
|
|
|
|
})
|
|
|
|
|
}) {
|
2022-08-30 13:28:36 +02:00
|
|
|
Some(output) => {
|
|
|
|
|
Box::new(std::iter::once(output.clone())) as Box<dyn Iterator<Item = Output>>
|
|
|
|
|
}
|
2023-01-23 18:25:01 +01:00
|
|
|
None => Box::new(
|
|
|
|
|
self.outputs()
|
|
|
|
|
.filter(|o| {
|
|
|
|
|
self.override_redirect_windows.iter().any(|or| {
|
2023-01-27 19:51:23 +01:00
|
|
|
if or.wl_surface().as_ref() == Some(surface) {
|
2023-10-25 19:24:51 +02:00
|
|
|
or.geometry()
|
|
|
|
|
.as_global()
|
|
|
|
|
.intersection(o.geometry())
|
|
|
|
|
.is_some()
|
2023-01-23 18:25:01 +01:00
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
.cloned()
|
|
|
|
|
.chain(self.outputs().map(|o| self.active_space(o)).flat_map(|w| {
|
|
|
|
|
w.mapped()
|
|
|
|
|
.find(|e| e.has_surface(surface, WindowSurfaceType::ALL))
|
2023-10-25 19:40:26 +02:00
|
|
|
.map(|_| w.output().clone())
|
2023-01-23 18:25:01 +01:00
|
|
|
.into_iter()
|
|
|
|
|
})),
|
|
|
|
|
),
|
2022-07-11 22:35:48 +02:00
|
|
|
}
|
2022-05-16 18:11:24 +02:00
|
|
|
}
|
|
|
|
|
|
2022-11-03 18:51:27 +01:00
|
|
|
pub fn workspaces_for_surface(
|
|
|
|
|
&self,
|
|
|
|
|
surface: &WlSurface,
|
|
|
|
|
) -> impl Iterator<Item = (WorkspaceHandle, Output)> {
|
2023-10-25 19:40:26 +02:00
|
|
|
match self.outputs().find(|o| {
|
2022-11-03 18:51:27 +01:00
|
|
|
let map = layer_map_for_output(o);
|
|
|
|
|
map.layer_for_surface(surface, WindowSurfaceType::ALL)
|
|
|
|
|
.is_some()
|
|
|
|
|
}) {
|
|
|
|
|
Some(output) => self
|
|
|
|
|
.workspaces
|
|
|
|
|
.spaces()
|
2023-10-25 19:40:26 +02:00
|
|
|
.find(move |workspace| workspace.output() == output)
|
|
|
|
|
.map(|w| (w.handle.clone(), output.clone())),
|
2022-11-03 18:51:27 +01:00
|
|
|
None => self
|
|
|
|
|
.workspaces
|
|
|
|
|
.spaces()
|
2023-10-25 19:40:26 +02:00
|
|
|
.find(|w| {
|
|
|
|
|
w.mapped()
|
|
|
|
|
.any(|e| e.has_surface(surface, WindowSurfaceType::ALL))
|
2022-11-03 18:51:27 +01:00
|
|
|
})
|
2023-10-25 19:40:26 +02:00
|
|
|
.map(|w| (w.handle.clone(), w.output().clone())),
|
2022-11-03 18:51:27 +01:00
|
|
|
}
|
|
|
|
|
.into_iter()
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-18 20:23:41 +01:00
|
|
|
pub fn element_for_surface(&self, surface: &CosmicSurface) -> Option<&CosmicMapped> {
|
2022-09-28 12:01:29 +02:00
|
|
|
self.workspaces
|
|
|
|
|
.spaces()
|
|
|
|
|
.find_map(|w| w.element_for_surface(surface))
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
2021-12-28 16:23:12 +01:00
|
|
|
|
2023-01-18 20:23:41 +01:00
|
|
|
pub fn element_for_wl_surface(&self, surface: &WlSurface) -> Option<&CosmicMapped> {
|
|
|
|
|
self.workspaces
|
|
|
|
|
.spaces()
|
|
|
|
|
.find_map(|w| w.element_for_wl_surface(surface))
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-28 12:01:29 +02:00
|
|
|
pub fn space_for(&self, mapped: &CosmicMapped) -> Option<&Workspace> {
|
|
|
|
|
self.workspaces
|
|
|
|
|
.spaces()
|
|
|
|
|
.find(|workspace| workspace.mapped().any(|m| m == mapped))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn space_for_mut(&mut self, mapped: &CosmicMapped) -> Option<&mut Workspace> {
|
|
|
|
|
self.workspaces
|
|
|
|
|
.spaces_mut()
|
|
|
|
|
.find(|workspace| workspace.mapped().any(|m| m == mapped))
|
2021-12-28 16:23:12 +01:00
|
|
|
}
|
2022-07-04 16:00:29 +02:00
|
|
|
|
2022-11-03 18:51:27 +01:00
|
|
|
pub fn space_for_handle(&self, handle: &WorkspaceHandle) -> Option<&Workspace> {
|
|
|
|
|
self.workspaces.spaces().find(|w| &w.handle == handle)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn space_for_handle_mut(&mut self, handle: &WorkspaceHandle) -> Option<&mut Workspace> {
|
|
|
|
|
self.workspaces.spaces_mut().find(|w| &w.handle == handle)
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 19:40:26 +02:00
|
|
|
pub fn outputs(&self) -> impl DoubleEndedIterator<Item = &Output> {
|
|
|
|
|
self.workspaces.sets.keys().chain(
|
|
|
|
|
self.workspaces
|
|
|
|
|
.backup_set
|
|
|
|
|
.as_ref()
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|set| &set.output),
|
|
|
|
|
)
|
2022-07-04 15:28:03 +02:00
|
|
|
}
|
2021-12-28 16:23:12 +01:00
|
|
|
|
2023-10-25 19:40:26 +02:00
|
|
|
pub fn global_space(&self) -> Rectangle<i32, Global> {
|
|
|
|
|
self.outputs()
|
2022-07-04 15:28:03 +02:00
|
|
|
.fold(
|
2023-10-25 19:40:26 +02:00
|
|
|
Option::<Rectangle<i32, Global>>::None,
|
2022-07-04 15:28:03 +02:00
|
|
|
|maybe_geo, output| match maybe_geo {
|
|
|
|
|
Some(rect) => Some(rect.merge(output.geometry())),
|
|
|
|
|
None => Some(output.geometry()),
|
|
|
|
|
},
|
|
|
|
|
)
|
2023-10-25 19:40:26 +02:00
|
|
|
.unwrap_or_else(Rectangle::default)
|
2022-07-04 15:28:03 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-12 20:01:37 +02:00
|
|
|
pub fn animations_going(&self) -> bool {
|
2023-10-25 19:40:26 +02:00
|
|
|
self.workspaces
|
|
|
|
|
.sets
|
|
|
|
|
.values()
|
|
|
|
|
.any(|set| set.previously_active.is_some())
|
|
|
|
|
|| !matches!(self.overview_mode, OverviewMode::None)
|
2023-06-28 19:20:06 +02:00
|
|
|
|| !matches!(self.resize_mode, ResizeMode::None)
|
2023-05-19 19:44:57 +02:00
|
|
|
|| self
|
|
|
|
|
.workspaces
|
|
|
|
|
.spaces()
|
|
|
|
|
.any(|workspace| workspace.animations_going())
|
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-05-12 20:01:37 +02:00
|
|
|
for workspace in self.workspaces.spaces_mut() {
|
2023-07-05 23:48:10 +02:00
|
|
|
clients.extend(workspace.update_animations());
|
2023-05-12 20:01:37 +02:00
|
|
|
}
|
2023-07-05 23:48:10 +02:00
|
|
|
clients
|
2023-05-12 20:01:37 +02:00
|
|
|
}
|
|
|
|
|
|
2023-08-11 18:15:22 +02:00
|
|
|
pub fn set_overview_mode(
|
|
|
|
|
&mut self,
|
|
|
|
|
enabled: Option<Trigger>,
|
2023-09-29 21:33:16 +02:00
|
|
|
evlh: LoopHandle<'static, crate::state::State>,
|
2023-08-11 18:15:22 +02:00
|
|
|
) {
|
2023-07-17 21:11:45 +02:00
|
|
|
if let Some(trigger) = enabled {
|
2023-05-19 19:44:57 +02:00
|
|
|
if !matches!(self.overview_mode, OverviewMode::Started(_, _)) {
|
2023-08-11 18:15:22 +02:00
|
|
|
if matches!(trigger, Trigger::KeyboardSwap(_, _)) {
|
|
|
|
|
self.swap_indicator = Some(swap_indicator(evlh));
|
|
|
|
|
}
|
2023-07-17 21:11:45 +02:00
|
|
|
self.overview_mode = OverviewMode::Started(trigger, Instant::now());
|
2023-05-19 19:44:57 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
2023-08-11 18:15:22 +02:00
|
|
|
if !matches!(self.overview_mode, OverviewMode::Ended(_, _)) {
|
|
|
|
|
let (reverse_duration, trigger) =
|
|
|
|
|
if let OverviewMode::Started(trigger, start) = self.overview_mode.clone() {
|
|
|
|
|
(
|
|
|
|
|
ANIMATION_DURATION
|
|
|
|
|
- Instant::now().duration_since(start).min(ANIMATION_DURATION),
|
|
|
|
|
Some(trigger),
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
(Duration::ZERO, None)
|
|
|
|
|
};
|
|
|
|
|
self.overview_mode =
|
|
|
|
|
OverviewMode::Ended(trigger, Instant::now() - reverse_duration);
|
2023-05-19 19:44:57 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-11 18:15:22 +02:00
|
|
|
pub fn overview_mode(&mut self) -> (OverviewMode, Option<SwapIndicator>) {
|
|
|
|
|
if let OverviewMode::Ended(_, timestamp) = self.overview_mode {
|
2023-05-19 19:44:57 +02:00
|
|
|
if Instant::now().duration_since(timestamp) > ANIMATION_DURATION {
|
|
|
|
|
self.overview_mode = OverviewMode::None;
|
2023-08-11 18:15:22 +02:00
|
|
|
self.swap_indicator = None;
|
2023-05-19 19:44:57 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-11 18:15:22 +02:00
|
|
|
(self.overview_mode.clone(), self.swap_indicator.clone())
|
2023-05-19 19:44:57 +02:00
|
|
|
}
|
|
|
|
|
|
2023-07-06 00:03:26 +02:00
|
|
|
pub fn set_resize_mode(
|
|
|
|
|
&mut self,
|
|
|
|
|
enabled: Option<(KeyPattern, ResizeDirection)>,
|
|
|
|
|
config: &Config,
|
2023-09-29 21:33:16 +02:00
|
|
|
evlh: LoopHandle<'static, crate::state::State>,
|
2023-07-06 00:03:26 +02:00
|
|
|
) {
|
2023-06-28 19:20:06 +02:00
|
|
|
if let Some((pattern, direction)) = enabled {
|
|
|
|
|
if let ResizeMode::Started(old_pattern, _, old_direction) = &mut self.resize_mode {
|
|
|
|
|
*old_pattern = pattern;
|
|
|
|
|
*old_direction = direction;
|
|
|
|
|
} else {
|
|
|
|
|
self.resize_mode = ResizeMode::Started(pattern, Instant::now(), direction);
|
|
|
|
|
}
|
2023-07-06 00:03:26 +02:00
|
|
|
self.resize_indicator = Some(resize_indicator(direction, config, evlh));
|
2023-06-28 19:20:06 +02:00
|
|
|
} else {
|
|
|
|
|
if let ResizeMode::Started(_, _, direction) = &self.resize_mode {
|
|
|
|
|
self.resize_mode = ResizeMode::Ended(Instant::now(), *direction);
|
2023-07-06 18:20:10 +02:00
|
|
|
if let Some((_, direction, edge, _, _, _)) = self.resize_state.as_ref() {
|
|
|
|
|
self.finish_resize(*direction, *edge);
|
|
|
|
|
}
|
2023-06-28 19:20:06 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-06 00:03:26 +02:00
|
|
|
pub fn resize_mode(&mut self) -> (ResizeMode, Option<ResizeIndicator>) {
|
2023-06-28 19:20:06 +02:00
|
|
|
if let ResizeMode::Ended(timestamp, _) = self.resize_mode {
|
|
|
|
|
if Instant::now().duration_since(timestamp) > ANIMATION_DURATION {
|
|
|
|
|
self.resize_mode = ResizeMode::None;
|
2023-07-06 00:03:26 +02:00
|
|
|
self.resize_indicator = None;
|
2023-06-28 19:20:06 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-06 00:03:26 +02:00
|
|
|
(self.resize_mode.clone(), self.resize_indicator.clone())
|
2023-06-28 21:29:39 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-28 12:01:29 +02:00
|
|
|
pub fn refresh(&mut self) {
|
2023-03-06 18:50:11 +01:00
|
|
|
#[cfg(feature = "debug")]
|
|
|
|
|
puffin::profile_function!();
|
|
|
|
|
|
2022-09-28 12:01:29 +02:00
|
|
|
self.popups.cleanup();
|
|
|
|
|
|
2023-10-25 19:40:26 +02:00
|
|
|
self.workspaces.refresh(
|
|
|
|
|
&mut self.workspace_state.update(),
|
2023-10-25 19:41:30 +02:00
|
|
|
&mut self.toplevel_info_state,
|
2023-10-25 19:40:26 +02:00
|
|
|
);
|
2022-09-28 12:01:29 +02:00
|
|
|
|
2023-10-25 19:40:26 +02:00
|
|
|
for output in self.outputs() {
|
2022-04-26 18:55:04 +02:00
|
|
|
let mut map = layer_map_for_output(output);
|
2022-09-28 12:01:29 +02:00
|
|
|
map.cleanup();
|
2021-12-22 20:14:36 +01:00
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
|
2023-01-27 19:51:23 +01:00
|
|
|
self.override_redirect_windows.retain(|or| or.alive());
|
2023-01-23 22:56:42 +01:00
|
|
|
self.override_redirect_windows
|
|
|
|
|
.iter()
|
2023-01-27 19:51:23 +01:00
|
|
|
.for_each(|or| or.refresh());
|
2023-01-23 18:25:01 +01:00
|
|
|
|
2022-07-04 16:00:29 +02:00
|
|
|
self.toplevel_info_state
|
|
|
|
|
.refresh(Some(&self.workspace_state));
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
2022-07-04 16:00:29 +02:00
|
|
|
|
2023-10-11 22:00:20 +02:00
|
|
|
pub fn remap_unfullscreened_window(
|
|
|
|
|
&mut self,
|
|
|
|
|
mapped: CosmicMapped,
|
|
|
|
|
current_workspace: &WorkspaceHandle,
|
|
|
|
|
previous_workspace: &WorkspaceHandle,
|
|
|
|
|
target_layer: ManagedLayer,
|
|
|
|
|
) {
|
|
|
|
|
if self.space_for_handle(previous_workspace).is_none() {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
let Some(workspace) = self.space_for_handle_mut(¤t_workspace) else { return };
|
|
|
|
|
let _ = workspace.unmap(&mapped);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let new_workspace_output = self
|
|
|
|
|
.space_for_handle(&previous_workspace)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.output()
|
|
|
|
|
.clone();
|
|
|
|
|
for (window, _) in mapped.windows() {
|
|
|
|
|
self.toplevel_info_state
|
|
|
|
|
.toplevel_enter_output(&window, &new_workspace_output);
|
|
|
|
|
self.toplevel_info_state
|
|
|
|
|
.toplevel_enter_workspace(&window, &previous_workspace);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let new_workspace = self.space_for_handle_mut(&previous_workspace).unwrap();
|
|
|
|
|
match target_layer {
|
|
|
|
|
ManagedLayer::Floating => new_workspace.floating_layer.map(mapped, None),
|
|
|
|
|
ManagedLayer::Tiling => {
|
|
|
|
|
new_workspace
|
|
|
|
|
.tiling_layer
|
|
|
|
|
.map(mapped, Option::<std::iter::Empty<_>>::None, None)
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn map_window(state: &mut State, window: &CosmicSurface) {
|
2022-08-31 13:01:23 +02:00
|
|
|
let pos = state
|
|
|
|
|
.common
|
|
|
|
|
.shell
|
2022-07-04 16:00:29 +02:00
|
|
|
.pending_windows
|
|
|
|
|
.iter()
|
2023-10-11 22:00:20 +02:00
|
|
|
.position(|(w, _, _)| w == window)
|
2022-07-04 16:00:29 +02:00
|
|
|
.unwrap();
|
2023-10-11 22:00:20 +02:00
|
|
|
let (window, seat, output) = state.common.shell.pending_windows.remove(pos);
|
2022-09-28 12:01:29 +02:00
|
|
|
|
2023-10-11 22:00:20 +02:00
|
|
|
let should_be_fullscreen = output.is_some();
|
|
|
|
|
let output = output.unwrap_or_else(|| seat.active_output());
|
|
|
|
|
|
|
|
|
|
let workspace = state.common.shell.workspaces.active_mut(&output);
|
|
|
|
|
if let Some((mapped, layer, previous_workspace)) = workspace.remove_fullscreen() {
|
|
|
|
|
let old_handle = workspace.handle.clone();
|
|
|
|
|
let new_workspace_handle = state
|
|
|
|
|
.common
|
|
|
|
|
.shell
|
|
|
|
|
.space_for_handle(&previous_workspace)
|
|
|
|
|
.is_some()
|
|
|
|
|
.then_some(previous_workspace)
|
|
|
|
|
.unwrap_or(old_handle);
|
|
|
|
|
|
|
|
|
|
state.common.shell.remap_unfullscreened_window(
|
|
|
|
|
mapped,
|
|
|
|
|
&old_handle,
|
|
|
|
|
&new_workspace_handle,
|
|
|
|
|
layer,
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let workspace = state.common.shell.workspaces.active_mut(&output);
|
2023-01-27 18:15:54 +01:00
|
|
|
state.common.shell.toplevel_info_state.new_toplevel(&window);
|
2022-08-31 13:01:23 +02:00
|
|
|
state
|
|
|
|
|
.common
|
|
|
|
|
.shell
|
|
|
|
|
.toplevel_info_state
|
2022-09-28 12:01:29 +02:00
|
|
|
.toplevel_enter_output(&window, &output);
|
2022-08-31 13:01:23 +02:00
|
|
|
state
|
|
|
|
|
.common
|
|
|
|
|
.shell
|
|
|
|
|
.toplevel_info_state
|
2022-09-28 12:01:29 +02:00
|
|
|
.toplevel_enter_workspace(&window, &workspace.handle);
|
|
|
|
|
|
2023-01-16 15:12:25 +01:00
|
|
|
let mapped = CosmicMapped::from(CosmicWindow::new(
|
|
|
|
|
window.clone(),
|
|
|
|
|
state.common.event_loop_handle.clone(),
|
|
|
|
|
));
|
2022-11-28 17:48:50 +01:00
|
|
|
#[cfg(feature = "debug")]
|
|
|
|
|
{
|
|
|
|
|
mapped.set_debug(state.common.egui.active);
|
|
|
|
|
}
|
2023-01-27 13:26:28 +01:00
|
|
|
if layout::should_be_floating(&window) || !workspace.tiling_enabled {
|
2023-10-25 19:40:26 +02:00
|
|
|
workspace.floating_layer.map(mapped.clone(), None);
|
2022-07-04 15:28:03 +02:00
|
|
|
} else {
|
2023-10-25 19:41:30 +02:00
|
|
|
for mapped in workspace
|
|
|
|
|
.mapped()
|
|
|
|
|
.filter(|m| m.maximized_state.lock().unwrap().is_some())
|
|
|
|
|
.cloned()
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
.into_iter()
|
|
|
|
|
{
|
|
|
|
|
workspace.unmaximize_request(&mapped.active_window());
|
|
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
let focus_stack = workspace.focus_stack.get(&seat);
|
|
|
|
|
workspace
|
|
|
|
|
.tiling_layer
|
2023-10-11 22:00:20 +02:00
|
|
|
.map(mapped.clone(), Some(focus_stack.iter()), None);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if should_be_fullscreen {
|
|
|
|
|
workspace.fullscreen_request(&mapped.active_window(), None);
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
2022-07-05 18:46:38 +02:00
|
|
|
|
2023-10-11 22:00:20 +02:00
|
|
|
if let CosmicSurface::X11(ref surface) = window {
|
2023-01-18 20:23:41 +01:00
|
|
|
if let Some(xwm) = state
|
|
|
|
|
.common
|
|
|
|
|
.xwayland_state
|
2023-03-07 20:28:41 +01:00
|
|
|
.as_mut()
|
|
|
|
|
.and_then(|state| state.xwm.as_mut())
|
2023-01-18 20:23:41 +01:00
|
|
|
{
|
2023-10-11 22:00:20 +02:00
|
|
|
if let Err(err) = xwm.raise_window(surface) {
|
2023-02-24 17:41:52 +01:00
|
|
|
warn!(?err, "Failed to update Xwayland stacking order.");
|
2023-01-18 20:23:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-28 12:01:29 +02:00
|
|
|
Shell::set_focus(state, Some(&KeyboardFocusTarget::from(mapped)), &seat, None);
|
2022-07-06 23:35:17 +02:00
|
|
|
|
2023-10-11 22:00:20 +02:00
|
|
|
let active_space = state.common.shell.active_space(&output);
|
2022-09-28 12:01:29 +02:00
|
|
|
for mapped in active_space.mapped() {
|
|
|
|
|
state.common.shell.update_reactive_popups(mapped);
|
2022-07-05 18:46:38 +02:00
|
|
|
}
|
2021-12-22 20:14:36 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-18 20:23:41 +01:00
|
|
|
pub fn map_override_redirect(state: &mut State, window: X11Surface) {
|
2023-01-23 22:56:42 +01:00
|
|
|
let geo = window.geometry();
|
2023-10-25 19:24:51 +02:00
|
|
|
for (output, overlap) in state.common.shell.outputs().cloned().filter_map(|o| {
|
|
|
|
|
o.geometry()
|
|
|
|
|
.as_logical()
|
|
|
|
|
.intersection(geo)
|
|
|
|
|
.map(|overlap| (o, overlap))
|
|
|
|
|
}) {
|
2023-01-23 22:56:42 +01:00
|
|
|
window.output_enter(&output, overlap);
|
|
|
|
|
}
|
2023-01-18 20:23:41 +01:00
|
|
|
|
2023-01-27 19:51:23 +01:00
|
|
|
state.common.shell.override_redirect_windows.push(window);
|
2023-01-18 20:23:41 +01:00
|
|
|
}
|
|
|
|
|
|
2022-08-31 13:01:23 +02:00
|
|
|
pub fn map_layer(state: &mut State, layer_surface: &LayerSurface) {
|
|
|
|
|
let pos = state
|
|
|
|
|
.common
|
|
|
|
|
.shell
|
2022-07-04 16:00:29 +02:00
|
|
|
.pending_layers
|
|
|
|
|
.iter()
|
|
|
|
|
.position(|(l, _, _)| l == layer_surface)
|
|
|
|
|
.unwrap();
|
2022-08-31 13:01:23 +02:00
|
|
|
let (layer_surface, output, seat) = state.common.shell.pending_layers.remove(pos);
|
2022-07-04 16:00:29 +02:00
|
|
|
|
2022-07-04 15:28:03 +02:00
|
|
|
let wants_focus = {
|
2022-09-28 12:01:29 +02:00
|
|
|
with_states(layer_surface.wl_surface(), |states| {
|
2022-07-04 15:28:03 +02:00
|
|
|
let state = states.cached_state.current::<LayerSurfaceCachedState>();
|
|
|
|
|
matches!(state.layer, Layer::Top | Layer::Overlay)
|
2022-07-15 14:22:25 +02:00
|
|
|
&& state.keyboard_interactivity != KeyboardInteractivity::None
|
2022-07-04 15:28:03 +02:00
|
|
|
})
|
|
|
|
|
};
|
2022-03-30 22:00:44 +02:00
|
|
|
|
2023-07-13 17:40:49 +02:00
|
|
|
{
|
|
|
|
|
let mut map = layer_map_for_output(&output);
|
|
|
|
|
map.map_layer(&layer_surface).unwrap();
|
|
|
|
|
}
|
|
|
|
|
for workspace in state.common.shell.workspaces.spaces_mut() {
|
2023-10-25 19:40:26 +02:00
|
|
|
workspace.tiling_layer.recalculate();
|
2023-07-13 17:40:49 +02:00
|
|
|
}
|
2022-07-04 15:28:03 +02:00
|
|
|
|
|
|
|
|
if wants_focus {
|
2022-09-28 12:01:29 +02:00
|
|
|
Shell::set_focus(state, Some(&layer_surface.into()), &seat, None)
|
2022-03-30 23:08:35 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-10 18:42:11 +01:00
|
|
|
pub fn move_current_window(
|
|
|
|
|
state: &mut State,
|
|
|
|
|
seat: &Seat<State>,
|
|
|
|
|
from_output: &Output,
|
|
|
|
|
to: (&Output, Option<usize>),
|
2023-01-24 19:22:00 +01:00
|
|
|
follow: bool,
|
2023-05-25 17:51:53 +02:00
|
|
|
direction: Option<Direction>,
|
2023-10-25 19:24:51 +02:00
|
|
|
) -> Result<Option<Point<i32, Global>>, InvalidWorkspaceIndex> {
|
2022-11-10 18:42:11 +01:00
|
|
|
let (to_output, to_idx) = to;
|
2023-05-22 20:19:11 +02:00
|
|
|
let to_idx = to_idx.unwrap_or(state.common.shell.workspaces.active_num(to_output).1);
|
2022-11-30 22:35:54 +01:00
|
|
|
if state
|
|
|
|
|
.common
|
|
|
|
|
.shell
|
|
|
|
|
.workspaces
|
|
|
|
|
.get(to_idx, to_output)
|
|
|
|
|
.is_none()
|
|
|
|
|
{
|
2023-05-25 00:10:24 +02:00
|
|
|
return Err(InvalidWorkspaceIndex);
|
2022-11-30 22:35:54 +01:00
|
|
|
}
|
2022-11-10 18:42:11 +01:00
|
|
|
|
|
|
|
|
if from_output == to_output
|
2023-05-22 20:19:11 +02:00
|
|
|
&& to_idx == state.common.shell.workspaces.active_num(from_output).1
|
2022-11-10 18:42:11 +01:00
|
|
|
{
|
2023-05-25 00:10:24 +02:00
|
|
|
return Ok(None);
|
2021-12-22 20:14:36 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-10 18:42:11 +01:00
|
|
|
let from_workspace = state.common.shell.workspaces.active_mut(from_output);
|
|
|
|
|
let maybe_window = from_workspace.focus_stack.get(seat).last().cloned();
|
|
|
|
|
|
2023-07-31 17:36:32 +02:00
|
|
|
let Some(mapped) = maybe_window else {
|
|
|
|
|
return Ok(None);
|
|
|
|
|
};
|
|
|
|
|
let Some(window_state) = from_workspace.unmap(&mapped) else {
|
|
|
|
|
return Ok(None);
|
|
|
|
|
};
|
2022-07-04 16:00:29 +02:00
|
|
|
|
2022-11-14 11:54:22 +01:00
|
|
|
for (toplevel, _) in mapped.windows() {
|
|
|
|
|
state
|
2022-11-10 18:42:11 +01:00
|
|
|
.common
|
|
|
|
|
.shell
|
2022-11-14 11:54:22 +01:00
|
|
|
.toplevel_info_state
|
|
|
|
|
.toplevel_leave_workspace(&toplevel, &from_workspace.handle);
|
2023-01-24 21:01:11 +01:00
|
|
|
if from_output != to_output {
|
|
|
|
|
state
|
|
|
|
|
.common
|
|
|
|
|
.shell
|
|
|
|
|
.toplevel_info_state
|
|
|
|
|
.toplevel_leave_output(&toplevel, from_output);
|
|
|
|
|
}
|
2022-11-14 11:54:22 +01:00
|
|
|
}
|
|
|
|
|
let elements = from_workspace.mapped().cloned().collect::<Vec<_>>();
|
2023-05-19 19:44:57 +02:00
|
|
|
|
2022-11-14 11:54:22 +01:00
|
|
|
for mapped in elements.into_iter() {
|
|
|
|
|
state.common.shell.update_reactive_popups(&mapped);
|
|
|
|
|
}
|
2023-01-24 19:22:00 +01:00
|
|
|
let new_pos = if follow {
|
|
|
|
|
seat.set_active_output(&to_output);
|
2023-05-25 00:10:24 +02:00
|
|
|
state.common.shell.activate(to_output, to_idx)?
|
2023-01-24 19:22:00 +01:00
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
2022-11-22 10:10:08 +01:00
|
|
|
|
2022-11-14 11:54:22 +01:00
|
|
|
let to_workspace = state
|
|
|
|
|
.common
|
|
|
|
|
.shell
|
|
|
|
|
.workspaces
|
|
|
|
|
.get_mut(to_idx, to_output)
|
|
|
|
|
.unwrap(); // checked above
|
|
|
|
|
let focus_stack = to_workspace.focus_stack.get(&seat);
|
2023-09-20 18:57:58 +02:00
|
|
|
if window_state.layer == ManagedLayer::Floating {
|
2023-10-25 19:41:30 +02:00
|
|
|
to_workspace.floating_layer.map(mapped.clone(), None);
|
2022-11-14 11:54:22 +01:00
|
|
|
} else {
|
|
|
|
|
to_workspace
|
|
|
|
|
.tiling_layer
|
2023-10-11 22:00:20 +02:00
|
|
|
.map(mapped.clone(), Some(focus_stack.iter()), direction);
|
2022-11-14 11:54:22 +01:00
|
|
|
}
|
2023-10-11 22:00:20 +02:00
|
|
|
let focus_target = if let Some(f) = window_state.was_fullscreen {
|
|
|
|
|
to_workspace.fullscreen_request(&mapped.active_window(), f.previously);
|
2023-10-25 19:41:30 +02:00
|
|
|
KeyboardFocusTarget::from(to_workspace.get_fullscreen().unwrap().clone())
|
|
|
|
|
} else {
|
|
|
|
|
KeyboardFocusTarget::from(mapped.clone())
|
2023-09-20 18:57:58 +02:00
|
|
|
};
|
|
|
|
|
|
2022-11-14 11:54:22 +01:00
|
|
|
for (toplevel, _) in mapped.windows() {
|
2023-01-24 21:01:11 +01:00
|
|
|
if from_output != to_output {
|
|
|
|
|
state
|
|
|
|
|
.common
|
|
|
|
|
.shell
|
|
|
|
|
.toplevel_info_state
|
|
|
|
|
.toplevel_enter_output(&toplevel, to_output);
|
|
|
|
|
}
|
2022-11-14 11:54:22 +01:00
|
|
|
state
|
|
|
|
|
.common
|
|
|
|
|
.shell
|
|
|
|
|
.toplevel_info_state
|
|
|
|
|
.toplevel_enter_workspace(&toplevel, &to_workspace.handle);
|
|
|
|
|
}
|
|
|
|
|
for mapped in to_workspace
|
|
|
|
|
.mapped()
|
|
|
|
|
.cloned()
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
.into_iter()
|
|
|
|
|
{
|
|
|
|
|
state.common.shell.update_reactive_popups(&mapped);
|
2022-07-04 16:00:29 +02:00
|
|
|
}
|
2022-11-14 11:54:22 +01:00
|
|
|
|
2023-01-24 19:22:00 +01:00
|
|
|
if follow {
|
2023-09-20 18:57:58 +02:00
|
|
|
Common::set_focus(state, Some(&focus_target), &seat, None);
|
2023-01-24 19:22:00 +01:00
|
|
|
}
|
2023-05-25 00:10:24 +02:00
|
|
|
Ok(new_pos)
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
2022-07-04 15:28:03 +02:00
|
|
|
|
2022-09-28 12:01:29 +02:00
|
|
|
pub fn update_reactive_popups(&self, mapped: &CosmicMapped) {
|
|
|
|
|
if let Some(workspace) = self.space_for(mapped) {
|
2023-10-25 19:24:51 +02:00
|
|
|
let element_loc = workspace
|
|
|
|
|
.element_geometry(mapped)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.loc
|
|
|
|
|
.to_global(&workspace.output);
|
2022-09-28 12:01:29 +02:00
|
|
|
for (toplevel, offset) in mapped.windows() {
|
2023-01-16 15:12:25 +01:00
|
|
|
if let CosmicSurface::Wayland(toplevel) = toplevel {
|
2023-10-25 19:24:51 +02:00
|
|
|
let window_geo_offset = toplevel.geometry().loc.as_global();
|
2023-01-16 15:12:25 +01:00
|
|
|
update_reactive_popups(
|
|
|
|
|
&toplevel,
|
2023-10-25 19:24:51 +02:00
|
|
|
element_loc + offset.as_global() + window_geo_offset,
|
|
|
|
|
self.outputs(),
|
2023-01-16 15:12:25 +01:00
|
|
|
);
|
|
|
|
|
}
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-18 20:23:41 +01:00
|
|
|
|
|
|
|
|
pub fn move_request(
|
|
|
|
|
state: &mut State,
|
|
|
|
|
surface: &WlSurface,
|
|
|
|
|
seat: &Seat<State>,
|
|
|
|
|
serial: impl Into<Option<Serial>>,
|
|
|
|
|
) {
|
|
|
|
|
let serial = serial.into();
|
|
|
|
|
if let Some(start_data) = check_grab_preconditions(&seat, surface, serial) {
|
|
|
|
|
if let Some(mapped) = state.common.shell.element_for_wl_surface(surface).cloned() {
|
|
|
|
|
if let Some(workspace) = state.common.shell.space_for_mut(&mapped) {
|
|
|
|
|
let output = seat.active_output();
|
|
|
|
|
let (window, _) = mapped
|
|
|
|
|
.windows()
|
|
|
|
|
.find(|(w, _)| w.wl_surface().as_ref() == Some(surface))
|
|
|
|
|
.unwrap();
|
2023-07-17 21:11:45 +02:00
|
|
|
let button = start_data.button;
|
2023-03-09 18:27:11 +01:00
|
|
|
if let Some(grab) = workspace.move_request(
|
|
|
|
|
&window,
|
|
|
|
|
&seat,
|
|
|
|
|
&output,
|
|
|
|
|
start_data,
|
|
|
|
|
state.common.config.static_conf.active_hint,
|
|
|
|
|
) {
|
2023-01-18 20:23:41 +01:00
|
|
|
let handle = workspace.handle;
|
|
|
|
|
state
|
|
|
|
|
.common
|
|
|
|
|
.shell
|
|
|
|
|
.toplevel_info_state
|
|
|
|
|
.toplevel_leave_workspace(&window, &handle);
|
|
|
|
|
state
|
|
|
|
|
.common
|
|
|
|
|
.shell
|
|
|
|
|
.toplevel_info_state
|
|
|
|
|
.toplevel_leave_output(&window, &output);
|
2023-07-17 21:11:45 +02:00
|
|
|
if grab.is_tiling_grab() {
|
2023-08-11 18:15:22 +02:00
|
|
|
state.common.shell.set_overview_mode(
|
|
|
|
|
Some(Trigger::Pointer(button)),
|
|
|
|
|
state.common.event_loop_handle.clone(),
|
|
|
|
|
);
|
2023-07-17 21:11:45 +02:00
|
|
|
}
|
2023-01-18 20:23:41 +01:00
|
|
|
seat.get_pointer().unwrap().set_grab(
|
|
|
|
|
state,
|
|
|
|
|
grab,
|
|
|
|
|
serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()),
|
|
|
|
|
Focus::Clear,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn resize_request(
|
|
|
|
|
state: &mut State,
|
|
|
|
|
surface: &WlSurface,
|
|
|
|
|
seat: &Seat<State>,
|
|
|
|
|
serial: impl Into<Option<Serial>>,
|
|
|
|
|
edges: ResizeEdge,
|
|
|
|
|
) {
|
|
|
|
|
let serial = serial.into();
|
|
|
|
|
if let Some(start_data) = check_grab_preconditions(&seat, surface, serial) {
|
|
|
|
|
if let Some(mapped) = state.common.shell.element_for_wl_surface(surface).cloned() {
|
|
|
|
|
if let Some(workspace) = state.common.shell.space_for_mut(&mapped) {
|
|
|
|
|
if let Some(grab) = workspace.resize_request(&mapped, &seat, start_data, edges)
|
|
|
|
|
{
|
|
|
|
|
seat.get_pointer().unwrap().set_grab(
|
|
|
|
|
state,
|
|
|
|
|
grab,
|
|
|
|
|
serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()),
|
|
|
|
|
Focus::Clear,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-07-05 23:57:38 +02:00
|
|
|
|
2023-07-06 18:20:10 +02:00
|
|
|
pub fn resize(&mut self, seat: &Seat<State>, direction: ResizeDirection, edge: ResizeEdge) {
|
2023-07-05 23:57:38 +02:00
|
|
|
let output = seat.active_output();
|
|
|
|
|
let (_, idx) = self.workspaces.active_num(&output);
|
2023-07-31 17:36:32 +02:00
|
|
|
let Some(focused) = seat.get_keyboard().unwrap().current_focus() else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
2023-07-05 23:57:38 +02:00
|
|
|
|
|
|
|
|
if let Some(workspace) = self.workspaces.get_mut(idx, &output) {
|
|
|
|
|
let amount = (self
|
|
|
|
|
.resize_state
|
|
|
|
|
.take()
|
|
|
|
|
.map(|(_, _, _, amount, _, _)| amount)
|
|
|
|
|
.unwrap_or(10)
|
|
|
|
|
+ 2)
|
|
|
|
|
.min(20);
|
|
|
|
|
if workspace.resize(&focused, direction, edge, amount) {
|
|
|
|
|
self.resize_state = Some((focused, direction, edge, amount, idx, output));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-06 18:20:10 +02:00
|
|
|
pub fn finish_resize(&mut self, direction: ResizeDirection, edge: ResizeEdge) {
|
2023-07-05 23:57:38 +02:00
|
|
|
if let Some((old_focused, old_direction, old_edge, _, idx, output)) =
|
|
|
|
|
self.resize_state.take()
|
|
|
|
|
{
|
|
|
|
|
let workspace = self.workspaces.get(idx, &output).unwrap();
|
|
|
|
|
if old_direction == direction && old_edge == edge {
|
2023-07-31 17:36:32 +02:00
|
|
|
let Some(toplevel) = old_focused.toplevel() else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
2023-07-05 23:57:38 +02:00
|
|
|
let Some(mapped) = workspace
|
|
|
|
|
.mapped()
|
|
|
|
|
.find(|m| m.has_surface(&toplevel, WindowSurfaceType::TOPLEVEL))
|
|
|
|
|
.cloned()
|
2023-07-31 17:36:32 +02:00
|
|
|
else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
2023-07-05 23:57:38 +02:00
|
|
|
let mut resize_state = mapped.resize_state.lock().unwrap();
|
|
|
|
|
if let Some(ResizeState::Resizing(data)) = *resize_state {
|
|
|
|
|
*resize_state = Some(ResizeState::WaitingForCommit(data));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-30 22:00:44 +02:00
|
|
|
}
|
2022-03-29 14:41:09 +02:00
|
|
|
|
2022-11-08 09:38:43 +01:00
|
|
|
fn workspace_set_idx<'a>(
|
2022-07-04 16:00:29 +02:00
|
|
|
state: &mut WorkspaceUpdateGuard<'a, State>,
|
2022-09-28 12:01:29 +02:00
|
|
|
idx: u8,
|
2022-11-22 17:09:49 +01:00
|
|
|
output_pos: usize,
|
2022-09-28 12:01:29 +02:00
|
|
|
handle: &WorkspaceHandle,
|
2022-07-04 15:28:03 +02:00
|
|
|
) {
|
2022-11-10 13:01:58 +01:00
|
|
|
state.set_workspace_name(&handle, format!("{}", idx));
|
2022-11-22 17:09:49 +01:00
|
|
|
state.set_workspace_coordinates(&handle, [Some(idx as u32), Some(output_pos as u32), None]);
|
2022-07-04 16:00:29 +02:00
|
|
|
}
|
2023-01-18 20:23:41 +01:00
|
|
|
|
|
|
|
|
pub fn check_grab_preconditions(
|
|
|
|
|
seat: &Seat<State>,
|
|
|
|
|
surface: &WlSurface,
|
|
|
|
|
serial: Option<Serial>,
|
|
|
|
|
) -> Option<PointerGrabStartData<State>> {
|
|
|
|
|
use smithay::reexports::wayland_server::Resource;
|
|
|
|
|
|
|
|
|
|
// TODO: touch resize.
|
|
|
|
|
let pointer = seat.get_pointer().unwrap();
|
|
|
|
|
|
|
|
|
|
// Check that this surface has a click grab.
|
|
|
|
|
if !match serial {
|
|
|
|
|
Some(serial) => pointer.has_grab(serial),
|
|
|
|
|
None => pointer.is_grabbed(),
|
|
|
|
|
} {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let start_data = pointer.grab_start_data().unwrap();
|
|
|
|
|
|
|
|
|
|
// If the focus was for a different surface, ignore the request.
|
|
|
|
|
if start_data.focus.is_none()
|
|
|
|
|
|| !start_data
|
|
|
|
|
.focus
|
|
|
|
|
.as_ref()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.0
|
|
|
|
|
.same_client_as(&surface.id())
|
|
|
|
|
{
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Some(start_data)
|
|
|
|
|
}
|