cosmic-comp/src/shell/workspace.rs

444 lines
15 KiB
Rust
Raw Normal View History

use crate::{
2022-09-28 12:01:29 +02:00
shell::{
element::CosmicWindow,
layout::{floating::FloatingLayout, tiling::TilingLayout},
},
state::State,
2022-09-28 12:01:29 +02:00
utils::prelude::*,
wayland::protocols::workspace::WorkspaceHandle,
};
2022-09-28 12:01:29 +02:00
use indexmap::IndexSet;
2022-03-24 20:32:31 +01:00
use smithay::{
2022-09-28 12:01:29 +02:00
backend::renderer::{
element::{surface::WaylandSurfaceRenderElement, AsRenderElements},
ImportAll, Renderer,
},
desktop::{
layer_map_for_output, space::SpaceElement, Kind, LayerSurface, Space, Window,
WindowSurfaceType,
},
2022-08-31 13:01:23 +02:00
input::{pointer::GrabStartData as PointerGrabStartData, Seat},
2022-09-28 12:01:29 +02:00
output::{Output, WeakOutput},
reexports::{
wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge},
2022-08-31 13:01:23 +02:00
wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle},
2022-03-24 20:32:31 +01:00
},
2022-09-28 12:01:29 +02:00
render_elements,
utils::{IsAlive, Logical, Point, Rectangle, Scale, Serial},
wayland::shell::wlr_layer::Layer,
};
use std::{collections::HashMap, time::Duration};
use super::{
element::CosmicMapped,
focus::{FocusStack, FocusStackMut},
layout::{floating::FloatingRenderElement, tiling::TilingRenderElement},
2022-03-24 20:32:31 +01:00
};
2022-03-30 22:00:44 +02:00
2022-09-28 12:01:29 +02:00
#[derive(Debug)]
2022-03-24 20:32:31 +01:00
pub struct Workspace {
pub tiling_layer: TilingLayout,
pub floating_layer: FloatingLayout,
2022-09-28 12:01:29 +02:00
pub tiling_enabled: bool,
pub fullscreen: HashMap<Output, Window>,
pub handle: WorkspaceHandle,
2022-09-28 12:01:29 +02:00
pub focus_stack: FocusStacks,
2022-03-24 20:32:31 +01:00
}
2022-09-28 12:01:29 +02:00
#[derive(Debug, Default)]
pub struct FocusStacks(HashMap<Seat<State>, IndexSet<CosmicMapped>>);
2022-03-24 20:32:31 +01:00
impl Workspace {
2022-09-28 12:01:29 +02:00
pub fn new(handle: WorkspaceHandle) -> Workspace {
2022-03-24 20:32:31 +01:00
Workspace {
tiling_layer: TilingLayout::new(),
floating_layer: FloatingLayout::new(),
2022-07-07 22:41:17 +02:00
tiling_enabled: true,
2022-04-22 15:18:28 +02:00
fullscreen: HashMap::new(),
handle,
2022-09-28 12:01:29 +02:00
focus_stack: FocusStacks::default(),
2022-03-24 20:32:31 +01:00
}
}
2022-09-28 12:01:29 +02:00
pub fn refresh(&mut self) {
self.fullscreen.retain(|_, w| w.alive());
2022-09-28 12:01:29 +02:00
self.floating_layer.refresh();
self.tiling_layer.refresh();
}
pub fn commit(&mut self, surface: &WlSurface) {
if let Some(mapped) = self.element_for_surface(surface) {
mapped
.windows()
.find(|(w, _)| w.toplevel().wl_surface() == surface)
.unwrap()
.0
.on_commit();
}
}
pub fn map_output(&mut self, output: &Output, position: Point<i32, Logical>) {
self.tiling_layer.map_output(output, position);
self.floating_layer.map_output(output, position);
}
pub fn unmap_output(&mut self, output: &Output) {
if let Some(dead_output_window) = self.fullscreen.remove(output) {
self.unfullscreen_request(&dead_output_window);
}
self.tiling_layer.unmap_output(output);
self.floating_layer.unmap_output(output);
self.refresh();
}
pub fn element_for_surface(&self, surface: &WlSurface) -> Option<&CosmicMapped> {
self.floating_layer
.mapped()
.chain(self.tiling_layer.mapped().map(|(_, w, _)| w))
.find(|e| {
e.windows()
.any(|(w, _)| w.toplevel().wl_surface() == surface)
})
}
2022-09-28 12:01:29 +02:00
pub fn outputs_for_element(&self, elem: &CosmicMapped) -> impl Iterator<Item = Output> {
self.floating_layer
.space
.outputs_for_element(elem)
.into_iter()
.chain(self.tiling_layer.output_for_element(elem).cloned())
}
pub fn output_under(&self, point: Point<i32, Logical>) -> Option<&Output> {
let space = &self.floating_layer.space;
space.outputs().find(|o| {
let internal_output_geo = space.output_geometry(o).unwrap();
let external_output_geo = o.geometry();
internal_output_geo.contains(point - external_output_geo.loc + internal_output_geo.loc)
})
}
pub fn element_under(
&self,
location: Point<f64, Logical>,
) -> Option<(&CosmicMapped, Point<i32, Logical>)> {
self.floating_layer
.space
.element_under(location)
.or_else(|| {
self.tiling_layer.mapped().find_map(|(_, mapped, loc)| {
let test_point = location - loc.to_f64() + mapped.geometry().loc.to_f64();
2022-09-28 12:01:29 +02:00
mapped
.is_in_input_region(&test_point)
.then_some((mapped, loc - mapped.geometry().loc))
2022-09-28 12:01:29 +02:00
})
})
}
pub fn element_geometry(&self, elem: &CosmicMapped) -> Option<Rectangle<i32, Logical>> {
let space = &self.floating_layer.space;
let outputs = space.outputs().collect::<Vec<_>>();
let offset = if outputs.len() == 1
&& space.output_geometry(&outputs[0]).unwrap().loc == Point::from((0, 0))
{
outputs[0].geometry().loc
} else {
(0, 0).into()
};
self.floating_layer
.space
.element_geometry(elem)
.or_else(|| self.tiling_layer.element_geometry(elem))
.map(|mut geo| {
geo.loc += offset;
geo
})
}
/*
pub fn maximize_request(&mut self, window: &CosmicWindow, output: &Output) {
2022-04-22 15:18:28 +02:00
if self.fullscreen.values().any(|w| w == window) {
return;
}
if self.floating_layer.windows.contains(window) {
2022-07-04 16:00:29 +02:00
self.floating_layer
2022-09-28 12:01:29 +02:00
.maximize_request(, window, output);
}
2022-03-24 20:32:31 +01:00
}
2022-09-28 12:01:29 +02:00
pub fn unmaximize_request(&mut self, window: &CosmicMapped) {
if self.fullscreen.values().any(|w| w == window) {
return self.unfullscreen_request(window);
}
if self.floating_layer.windows.contains(window) {
self.floating_layer
2022-09-28 12:01:29 +02:00
.unmaximize_request(window);
}
}
2022-03-24 20:32:31 +01:00
pub fn resize_request(
2022-08-31 13:01:23 +02:00
state: &mut State,
surface: &WlSurface,
seat: &Seat<State>,
2022-03-24 20:32:31 +01:00
serial: Serial,
2022-08-31 13:01:23 +02:00
start_data: PointerGrabStartData<State>,
2022-03-24 20:32:31 +01:00
edges: ResizeEdge,
) {
2022-08-31 13:01:23 +02:00
let workspace = state.common.shell.space_for_window_mut(surface).unwrap();
let window = workspace
.space
.window_for_surface(surface, WindowSurfaceType::TOPLEVEL)
.unwrap()
.clone();
if workspace.fullscreen.values().any(|w| w == &window) {
2022-04-22 15:18:28 +02:00
return;
}
2022-08-31 13:01:23 +02:00
if workspace.floating_layer.windows.contains(&window) {
FloatingLayout::resize_request(state, &window, seat, serial, start_data.clone(), edges)
} else if workspace.tiling_layer.windows.contains(&window) {
TilingLayout::resize_request(state, &window, seat, serial, start_data, edges)
}
2022-03-24 20:32:31 +01:00
}
2022-09-28 12:01:29 +02:00
*/
2022-04-22 15:18:28 +02:00
pub fn fullscreen_request(&mut self, window: &Window, output: &Output) {
2022-09-28 12:01:29 +02:00
if self.fullscreen.contains_key(output) {
2022-04-22 15:18:28 +02:00
return;
}
#[allow(irrefutable_let_patterns)]
if let Kind::Xdg(xdg) = &window.toplevel() {
xdg.with_pending_state(|state| {
state.states.set(xdg_toplevel::State::Fullscreen);
state.size = Some(
output
.current_mode()
.map(|m| m.size)
.unwrap_or((0, 0).into())
.to_f64()
.to_logical(output.current_scale().fractional_scale())
.to_i32_round(),
);
});
xdg.send_configure();
2022-09-28 12:01:29 +02:00
self.fullscreen.insert(output.clone(), window.clone());
2022-04-22 15:18:28 +02:00
}
}
2022-05-03 13:37:51 +02:00
2022-04-22 15:18:28 +02:00
pub fn unfullscreen_request(&mut self, window: &Window) {
if self.fullscreen.values().any(|w| w == window) {
#[allow(irrefutable_let_patterns)]
if let Kind::Xdg(xdg) = &window.toplevel() {
xdg.with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Fullscreen);
state.size = None;
});
2022-09-28 12:01:29 +02:00
self.floating_layer.refresh();
self.tiling_layer.refresh();
xdg.send_configure();
2022-04-22 15:18:28 +02:00
}
self.fullscreen.retain(|_, w| w != window);
}
}
pub fn fullscreen_toggle(&mut self, window: &Window, output: &Output) {
2022-09-28 12:01:29 +02:00
if self.fullscreen.contains_key(output) {
2022-04-22 15:18:28 +02:00
self.unfullscreen_request(window)
} else {
self.fullscreen_request(window, output)
}
}
pub fn get_fullscreen(&self, output: &Output) -> Option<&Window> {
2022-09-28 12:01:29 +02:00
self.fullscreen.get(output).filter(|w| w.alive())
2022-04-22 15:18:28 +02:00
}
2022-07-07 22:41:17 +02:00
pub fn toggle_tiling(&mut self, seat: &Seat<State>) {
if self.tiling_enabled {
2022-09-28 12:01:29 +02:00
for window in self
.tiling_layer
.mapped()
.map(|(_, m, _)| m.clone())
.collect::<Vec<_>>()
.into_iter()
{
self.tiling_layer.unmap(&window);
self.floating_layer.map(window, seat, None);
2022-07-07 22:41:17 +02:00
}
self.tiling_enabled = false;
} else {
2022-09-28 12:01:29 +02:00
let focus_stack = self.focus_stack.get(seat);
for window in self
.floating_layer
.mapped()
.cloned()
.collect::<Vec<_>>()
.into_iter()
{
self.floating_layer.unmap(&window);
self.tiling_layer.map(window, seat, focus_stack.iter())
2022-07-07 22:41:17 +02:00
}
self.tiling_enabled = true;
}
}
pub fn toggle_floating_window(&mut self, seat: &Seat<State>) {
if self.tiling_enabled {
2022-09-28 12:01:29 +02:00
if let Some(window) = self.focus_stack.get(seat).iter().next().cloned() {
if self.tiling_layer.mapped().any(|(_, m, _)| m == &window) {
self.tiling_layer.unmap(&window);
self.floating_layer.map(window, seat, None);
} else if self.floating_layer.mapped().any(|w| w == &window) {
let focus_stack = self.focus_stack.get(seat);
self.floating_layer.unmap(&window);
self.tiling_layer.map(window, seat, focus_stack.iter())
2022-07-07 22:41:17 +02:00
}
}
}
}
2022-09-28 12:01:29 +02:00
pub fn mapped(&self) -> impl Iterator<Item = &CosmicMapped> {
self.floating_layer
.mapped()
.chain(self.tiling_layer.mapped().map(|(_, w, _)| w))
}
pub fn windows(&self) -> impl Iterator<Item = Window> + '_ {
self.floating_layer
.windows()
.chain(self.tiling_layer.windows().map(|(_, w, _)| w))
}
pub fn render_output<R>(
&self,
output: &Output,
) -> Result<Vec<WorkspaceRenderElement<R>>, OutputNotMapped>
where
R: Renderer + ImportAll,
<R as Renderer>::TextureId: 'static,
{
let mut render_elements = Vec::new();
let output_scale = output.current_scale().fractional_scale();
let layer_map = layer_map_for_output(output);
if let Some(fullscreen) = self.fullscreen.get(output) {
// overlay layer surfaces
render_elements.extend(
layer_map
.layers()
.rev()
.filter(|s| s.layer() == Layer::Overlay)
.filter_map(|surface| {
layer_map
.layer_geometry(surface)
.map(|geo| (geo.loc, surface))
})
.flat_map(|(loc, surface)| {
AsRenderElements::<R>::render_elements::<WorkspaceRenderElement<R>>(
surface,
loc.to_physical_precise_round(output_scale),
Scale::from(output_scale),
)
}),
);
// fullscreen window
render_elements.extend(AsRenderElements::<R>::render_elements::<
WorkspaceRenderElement<R>,
>(fullscreen, (0, 0).into(), output_scale.into()));
} else {
// TODO: Handle modes like
// - keyboard window swapping
// - resizing / moving in tiling
// overlay and top layer surfaces
let lower = {
let (upper, lower): (Vec<&LayerSurface>, Vec<&LayerSurface>) = layer_map
.layers()
.rev()
.partition(|s| matches!(s.layer(), Layer::Background | Layer::Bottom));
render_elements.extend(
upper
.into_iter()
.filter_map(|surface| {
layer_map
.layer_geometry(surface)
.map(|geo| (geo.loc, surface))
})
.flat_map(|(loc, surface)| {
AsRenderElements::<R>::render_elements::<WorkspaceRenderElement<R>>(
surface,
loc.to_physical_precise_round(output_scale),
Scale::from(output_scale),
)
}),
);
lower
};
// floating surfaces
render_elements.extend(
self.floating_layer
.render_output::<R>(output)?
.into_iter()
.map(WorkspaceRenderElement::from),
);
//tiling surfaces
render_elements.extend(
self.tiling_layer
.render_output::<R>(output)?
.into_iter()
.map(WorkspaceRenderElement::from),
);
// bottom and background layer surfaces
{
render_elements.extend(
lower
.into_iter()
.filter_map(|surface| {
layer_map
.layer_geometry(surface)
.map(|geo| (geo.loc, surface))
})
.flat_map(|(loc, surface)| {
AsRenderElements::<R>::render_elements::<WorkspaceRenderElement<R>>(
surface,
loc.to_physical_precise_round(output_scale),
Scale::from(output_scale),
)
}),
);
}
}
Ok(render_elements)
}
}
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;
render_elements! {
pub WorkspaceRenderElement<R> where R: ImportAll;
Wayland=WaylandSurfaceRenderElement,
Floating=FloatingRenderElement<R>,
Tiling=TilingRenderElement<R>,
2022-03-24 20:32:31 +01:00
}