use std::rc::Rc;
use std::time::Duration;
use niri_config::{OutputName, Workspace as WorkspaceConfig};
use niri_ipc::SizeChange;
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::desktop::{layer_map_for_output, Window};
use smithay::output::Output;
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
use smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size, Transform};
use super::scrolling::{
Column, ColumnWidth, InsertHint, InsertPosition, ScrollingSpace, ScrollingSpaceRenderElement,
};
use super::tile::{Tile, TileRenderSnapshot};
use super::{InteractiveResizeData, LayoutElement, Options, RemovedTile};
use crate::animation::Clock;
use crate::niri_render_elements;
use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::RenderTarget;
use crate::utils::id::IdCounter;
use crate::utils::transaction::{Transaction, TransactionBlocker};
use crate::utils::{output_size, send_scale_transform, ResizeEdge};
use crate::window::ResolvedWindowRules;
#[derive(Debug)]
pub struct Workspace<W: LayoutElement> {
/// The scrollable-tiling layout.
scrolling: ScrollingSpace<W>,
/// The original output of this workspace.
///
/// Most of the time this will be the workspace's current output, however, after an output
/// disconnection, it may remain pointing to the disconnected output.
pub(super) original_output: OutputId,
/// Current output of this workspace.
output: Option<Output>,
/// Latest known output scale for this workspace.
///
/// This should be set from the current workspace output, or, if all outputs have been
/// disconnected, preserved until a new output is connected.
scale: smithay::output::Scale,
/// Latest known output transform for this workspace.
///
/// This should be set from the current workspace output, or, if all outputs have been
/// disconnected, preserved until a new output is connected.
transform: Transform,
/// Latest known view size for this workspace.
///
/// This should be computed from the current workspace output size, or, if all outputs have
/// been disconnected, preserved until a new output is connected.
view_size: Size<f64, Logical>,
/// Latest known working area for this workspace.
///
/// Not rounded to physical pixels.
///
/// This is similar to view size, but takes into account things like layer shell exclusive
/// zones.
working_area: Rectangle<f64, Logical>,
/// Clock for driving animations.
pub(super) clock: Clock,
/// Configurable properties of the layout as received from the parent monitor.
pub(super) base_options: Rc<Options>,
/// Configurable properties of the layout with logical sizes adjusted for the current `scale`.
pub(super) options: Rc<Options>,
/// Optional name of this workspace.
pub(super) name: Option<String>,
/// Unique ID of this workspace.
id: WorkspaceId,
}
#[derive(Debug, Clone)]
pub struct OutputId(String);
impl OutputId {
pub fn matches(&self, output: &Output) -> bool {
let output_name = output.user_data().get::<OutputName>().unwrap();
output_name.matches(&self.0)
}
}
static WORKSPACE_ID_COUNTER: IdCounter = IdCounter::new();
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WorkspaceId(u64);
impl WorkspaceId {
fn next() -> WorkspaceId {
WorkspaceId(WORKSPACE_ID_COUNTER.next())
}
pub fn get(self) -> u64 {
self.0
}
pub fn specific(id: u64) -> Self {
Self(id)
}
}
niri_render_elements! {
WorkspaceRenderElement<R> => {
Scrolling = ScrollingSpaceRenderElement<R>,
}
}
#[derive(Debug)]
pub(super) struct InteractiveResize<W: LayoutElement> {
pub window: W::Id,
pub original_window_size: Size<f64, Logical>,
pub data: InteractiveResizeData,
}
/// Resolved width or height in logical pixels.
#[derive(Debug, Clone, Copy)]
pub enum ResolvedSize {
/// Size of the tile including borders.
Tile(f64),
/// Size of the window excluding borders.
Window(f64),
}
impl OutputId {
pub fn new(output: &Output) -> Self {
let output_name = output.user_data().get::<OutputName>().unwrap();
Self(output_name.format_make_model_serial_or_connector())
}
}
impl<W: LayoutElement> Workspace<W> {
pub fn new(output: Output, clock: Clock, options: Rc<Options>) -> Self {
Self::new_with_config(output, None, clock, options)
}
pub fn new_with_config(
output: Output,
config: Option<WorkspaceConfig>,
clock: Clock,
base_options: Rc<Options>,
) -> Self {
let original_output = config
.as_ref()
.and_then(|c|