From 00f1b029da61d1cedf678a33bb21831fe773778e Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 28 Sep 2022 12:01:29 +0200 Subject: [PATCH] wip: New shell logic --- Cargo.lock | 7 - Cargo.toml | 1 - config.ron | 3 +- src/backend/kms/mod.rs | 55 +- src/backend/mod.rs | 26 +- src/backend/render/cursor.rs | 222 ++- src/backend/render/mod.rs | 423 ++---- src/backend/winit.rs | 46 +- src/backend/x11.rs | 25 +- src/config/mod.rs | 6 +- src/input/mod.rs | 342 ++--- src/main.rs | 2 +- src/shell/element/mod.rs | 631 +++++++++ src/shell/element/stack.rs | 293 ++++ src/shell/element/window.rs | 231 ++++ src/shell/focus/mod.rs | 231 ++-- src/shell/focus/target.rs | 274 +++- src/shell/grabs.rs | 11 +- src/shell/layout/floating/grabs.rs | 9 +- src/shell/layout/floating/mod.rs | 322 +++-- src/shell/layout/mod.rs | 19 +- src/shell/layout/tiling/mod.rs | 1165 ++++++++++------ src/shell/mod.rs | 1223 +++++++++-------- src/shell/workspace.rs | 367 ++++- src/state.rs | 152 +- src/utils/prelude.rs | 32 +- src/wayland/handlers/compositor.rs | 11 +- src/wayland/handlers/export_dmabuf.rs | 2 +- src/wayland/handlers/layer_shell.rs | 4 +- src/wayland/handlers/mod.rs | 2 +- src/wayland/handlers/seat.rs | 26 +- src/wayland/handlers/toplevel_management.rs | 33 +- src/wayland/handlers/workspace.rs | 15 +- src/wayland/handlers/xdg_shell/mod.rs | 113 +- src/wayland/handlers/xdg_shell/popup.rs | 86 +- src/wayland/protocols/mod.rs | 2 +- src/wayland/protocols/output_configuration.rs | 5 +- src/wayland/protocols/toplevel_info.rs | 4 +- src/wayland/protocols/workspace.rs | 4 +- 39 files changed, 3922 insertions(+), 2503 deletions(-) create mode 100644 src/shell/element/mod.rs create mode 100644 src/shell/element/stack.rs create mode 100644 src/shell/element/window.rs diff --git a/Cargo.lock b/Cargo.lock index 3e15fee6..df0d2da0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,12 +83,6 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164" -[[package]] -name = "atomic_float" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62af46d040ba9df09edc6528dae9d8e49f5f3e82f55b7d2ec31a733c38dbc49d" - [[package]] name = "atomic_refcell" version = "0.1.8" @@ -292,7 +286,6 @@ name = "cosmic-comp" version = "0.1.0" dependencies = [ "anyhow", - "atomic_float", "bitflags", "cosmic-protocols", "edid-rs", diff --git a/Cargo.toml b/Cargo.toml index d351a463..43310f4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,6 @@ xkbcommon = "0.4" indexmap = "1.8.0" xdg = "^2.1" ron = "0.7" -atomic_float = "0.1" libsystemd = "0.5" wayland-backend = "=0.1.0-beta.10" wayland-scanner = "=0.30.0-beta.10" diff --git a/config.ron b/config.ron index 5a28839b..f8a583c0 100644 --- a/config.ron +++ b/config.ron @@ -37,7 +37,7 @@ (modifiers: [Logo], key: "y"): ToggleTiling, (modifiers: [Logo], key: "g"): ToggleWindowFloating, (modifiers: [Logo, Shift], key: "f"): Fullscreen, - (modifiers: [Logo, Shift], key: "s"): Screenshot, + //(modifiers: [Logo, Shift], key: "s"): Screenshot, //TODO: ability to select default web browser (modifiers: [Logo], key: "b"): Spawn("firefox"), //TODO: ability to select default file browser @@ -53,5 +53,6 @@ (modifiers: [], key: "XF86MonBrightnessDown"): Spawn("busctl --user call com.system76.CosmicSettingsDaemon /com/system76/CosmicSettingsDaemon com.system76.CosmicSettingsDaemon DecreaseDisplayBrightness"), }, workspace_mode: OutputBound, + workspace_amount: Dynamic, floating_default: false, ) diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index fd860432..bdb69036 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -20,7 +20,8 @@ use smithay::{ input::InputEvent, libinput::{LibinputInputBackend, LibinputSessionInterface}, renderer::{ - gles2::Gles2Renderbuffer, + damage::DamageTrackedRenderer, + gles2::{Gles2Renderbuffer, Gles2Renderer}, multigpu::{egl::EglGlesBackend, GpuManager}, Bind, }, @@ -51,7 +52,7 @@ use std::{ os::unix::io::{FromRawFd, OwnedFd}, path::PathBuf, rc::Rc, - time::{Duration, Instant}, + time::Duration, }; mod drm_helpers; @@ -60,9 +61,11 @@ mod socket; use session_fd::*; use socket::*; +use super::render::GlMultiRenderer; + pub struct KmsState { devices: HashMap, - pub api: GpuManager, + pub api: GpuManager>, pub primary: DrmNode, session: AutoSession, signaler: Signaler, @@ -83,9 +86,9 @@ pub struct Device { pub struct Surface { surface: Option>>, SessionFd>>, + damage_tracker: DamageTrackedRenderer, connector: connector::Handle, output: Output, - last_render: Option<(Dmabuf, Instant)>, last_submit: Option, refresh_rate: u32, vrr: bool, @@ -142,7 +145,8 @@ pub fn init_backend( .map_err(|err| err.error) .context("Failed to initialize session event source")?; - let api = GpuManager::new(EglGlesBackend, None).context("Failed to initialize renderers")?; + let api = GpuManager::new(EglGlesBackend::::default(), None) + .context("Failed to initialize renderers")?; // TODO get this info from system76-power, if available and setup a watcher let primary = if let Some(path) = std::env::var("COSMIC_RENDER_DEVICE") @@ -370,15 +374,7 @@ impl State { Some(Ok(_)) => { surface.last_submit = metadata.take().map(|data| data.time); surface.pending = false; - data.state - .common - .shell - .active_space_mut(&surface.output) - .space - .send_frames( - data.state.common.start_time.elapsed().as_millis() - as u32, - ); + data.state.common.send_frames(&surface.output); } Some(Err(err)) => { slog_scope::warn!("Failed to submit frame: {}", err) @@ -689,12 +685,12 @@ impl Device { let data = Surface { output: output.clone(), + damage_tracker: DamageTrackedRenderer::from_output(&output), surface: None, connector: conn, vrr, refresh_rate, last_submit: None, - last_render: None, pending: false, render_timer_token: None, #[cfg(feature = "debug")] @@ -717,8 +713,8 @@ fn render_node_for_output( let workspace = shell.active_space(output); let nodes = workspace .get_fullscreen(output) - .map(|w| vec![w]) - .unwrap_or_else(|| workspace.space.windows().collect::>()) + .map(|w| vec![w.clone()]) + .unwrap_or_else(|| workspace.windows().collect::>()) .into_iter() .flat_map(|w| { dh.get_client(w.toplevel().wl_surface().id()) @@ -750,7 +746,7 @@ impl Surface { pub fn render_output( &mut self, dh: &DisplayHandle, - api: &mut GpuManager, + api: &mut GpuManager>, target_node: &DrmNode, state: &mut Common, ) -> Result<()> { @@ -758,12 +754,8 @@ impl Surface { return Ok(()); } - if render::needs_buffer_reset(&self.output, state) { - self.surface.as_mut().unwrap().reset_buffers(); - } - let render_node = render_node_for_output(dh, &self.output, *target_node, &state.shell); - let mut renderer = api.renderer(&render_node, &target_node).unwrap(); + let mut renderer: GlMultiRenderer = api.renderer(&render_node, &target_node).unwrap(); let surface = self.surface.as_mut().unwrap(); let (buffer, age) = surface @@ -777,7 +769,8 @@ impl Surface { match render::render_output( Some(&render_node), &mut renderer, - age, + &mut self.damage_tracker, + age as usize, state, &self.output, false, @@ -785,7 +778,6 @@ impl Surface { Some(&mut self.fps), ) { Ok(_) => { - self.last_render = Some((buffer, Instant::now())); surface .queue_buffer() .with_context(|| "Failed to submit buffer for display")?; @@ -1026,17 +1018,4 @@ impl KmsState { } Ok(()) } - - pub fn capture_output(&self, output: &Output) -> Option<(DrmNode, Dmabuf, Instant)> { - self.devices.values().find_map(|dev| { - dev.surfaces - .values() - .find(|s| &s.output == output) - .and_then(|s| { - s.last_render - .clone() - .map(|(buf, time)| (dev.render_node.clone(), buf, time)) - }) - }) - } } diff --git a/src/backend/mod.rs b/src/backend/mod.rs index a3b198eb..b90ff5c5 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -39,18 +39,22 @@ pub fn init_backend_auto( } } }; + if res.is_ok() { - for seat in &state.common.seats { - let output = state - .common - .shell - .outputs() - .next() - .with_context(|| "Backend initialized without output")? - .clone(); - seat.user_data() - .insert_if_missing(|| crate::input::ActiveOutput(std::cell::RefCell::new(output))); - } + let output = state + .common + .shell + .outputs() + .next() + .with_context(|| "Backend initialized without output")?; + let initial_seat = crate::input::add_seat( + dh, + &mut state.common.seat_state, + output, + &state.common.config, + "seat-0".into(), + ); + state.common.add_seat(initial_seat); } res } diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index 7b5e8715..64182f44 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -2,14 +2,20 @@ use crate::utils::prelude::*; use smithay::{ - backend::renderer::{Frame, ImportAll, ImportMem, Renderer, Texture}, - desktop::space::{RenderElement, SpaceOutputTuple, SurfaceTree}, + backend::renderer::{ + element::{ + surface::{render_elements_from_surface_tree, WaylandSurfaceRenderElement}, + texture::{TextureBuffer, TextureRenderElement}, + }, + ImportAll, ImportMem, Renderer, + }, input::{ pointer::{CursorImageAttributes, CursorImageStatus}, Seat, }, reexports::wayland_server::protocol::wl_surface, - utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Size, Transform}, + render_elements, + utils::{IsAlive, Logical, Point, Scale, Transform}, wayland::compositor::{get_role, with_states}, }; use std::{ @@ -119,13 +125,21 @@ fn load_icon(theme: &CursorTheme) -> Result, Error> { parse_xcursor(&cursor_data).ok_or(Error::Parse) } -pub fn draw_surface_cursor( - surface: wl_surface::WlSurface, +render_elements! { + pub CursorRenderElement where R: ImportAll; + Static=TextureRenderElement<::TextureId>, + Surface=WaylandSurfaceRenderElement, +} + +pub fn draw_surface_cursor( + surface: &wl_surface::WlSurface, location: impl Into>, -) -> SurfaceTree + scale: impl Into>, +) -> Vec> where { let mut position = location.into(); + let scale = scale.into(); let h = with_states(&surface, |states| { states .data_map @@ -136,125 +150,26 @@ where .hotspot }); position -= h; - SurfaceTree { - surface, - position, - z_index: 100, - } + + render_elements_from_surface_tree(surface, position.to_physical_precise_round(scale), scale) } -pub fn draw_dnd_icon( - surface: wl_surface::WlSurface, +pub fn draw_dnd_icon( + surface: &wl_surface::WlSurface, location: impl Into>, -) -> SurfaceTree { + scale: impl Into>, +) -> Vec> { if get_role(&surface) != Some("dnd_icon") { slog_scope::warn!( "Trying to display as a dnd icon a surface that does not have the DndIcon role." ); } - SurfaceTree { + let scale = scale.into(); + render_elements_from_surface_tree( surface, - position: location.into(), - z_index: 100, - } -} - -pub struct PointerElement { - seat_id: usize, - texture: T, - position: Point, - size: Size, - new_frame: bool, -} - -impl PointerElement { - pub fn new( - seat: &Seat, - texture: T, - relative_pointer_pos: Point, - new_frame: bool, - ) -> PointerElement { - let size = texture.size().to_logical(1, Transform::Normal); - PointerElement { - seat_id: seat.id(), - texture, - position: relative_pointer_pos, - size, - new_frame, - } - } -} - -impl RenderElement for PointerElement<::TextureId> -where - R: Renderer + ImportAll, - ::TextureId: 'static, -{ - fn id(&self) -> usize { - self.seat_id - } - - fn location(&self, scale: impl Into>) -> Point { - self.position.to_physical(scale) - } - - fn geometry(&self, scale: impl Into>) -> Rectangle { - Rectangle::from_loc_and_size(self.position, self.size.to_f64()) - .to_physical(scale) - .to_i32_round() - } - - fn accumulated_damage( - &self, - scale: impl Into>, - _: Option>, - ) -> Vec> { - if self.new_frame { - let scale = scale.into(); - vec![Rectangle::from_loc_and_size( - self.position.to_physical(scale).to_i32_round(), - self.size.to_physical_precise_round(scale), - )] - } else { - vec![] - } - } - - fn opaque_regions( - &self, - _scale: impl Into>, - ) -> Option>> { - None - } - - fn draw( - &self, - _renderer: &mut R, - frame: &mut ::Frame, - scale: impl Into>, - position: Point, - damage: &[Rectangle], - _log: &slog::Logger, - ) -> Result<(), ::Error> { - let scale = scale.into(); - frame.render_texture_at( - &self.texture, - position.to_i32_round(), - 1, - scale, - Transform::Normal, - &damage - .iter() - .copied() - .map(|mut rect| { - rect.loc -= self.position.to_physical(scale).to_i32_round(); - rect - }) - .collect::>(), - 1.0, - )?; - Ok(()) - } + location.into().to_physical_precise_round(scale), + scale, + ) } struct CursorState { @@ -273,15 +188,15 @@ impl Default for CursorState { } } -pub fn draw_cursor( +pub fn draw_cursor( renderer: &mut R, seat: &Seat, location: Point, + scale: Scale, start_time: &std::time::Instant, draw_default: bool, -) -> Option +) -> Vec> where - I: From + From::TextureId>>, R: Renderer + ImportAll + ImportMem, ::TextureId: Clone + 'static, { @@ -302,50 +217,69 @@ where }) .unwrap_or(CursorImageStatus::Default); - if let CursorImageStatus::Surface(wl_surface) = cursor_status { - Some(draw_surface_cursor(wl_surface.clone(), location.to_i32_round()).into()) + if let CursorImageStatus::Surface(ref wl_surface) = cursor_status { + return draw_surface_cursor(wl_surface, location.to_i32_round(), scale); } else if draw_default { + let integer_scale = scale.x.max(scale.y).ceil() as u32; + let seat_userdata = seat.user_data(); seat_userdata.insert_if_missing(CursorState::default); let state = seat_userdata.get::().unwrap(); let frame = state .cursor - .get_image(1, start_time.elapsed().as_millis() as u32); - let new_frame = state.current_image.borrow().as_ref() != Some(&frame); + .get_image(integer_scale, start_time.elapsed().as_millis() as u32); let mut cache = state.image_cache.borrow_mut(); let pointer_images = cache - .entry((TypeId::of::<::TextureId>(), renderer.id())) + .entry(( + TypeId::of::::TextureId>>(), + renderer.id(), + )) .or_default(); - let pointer_image = pointer_images + + let maybe_image = pointer_images .iter() .find_map(|(image, texture)| if image == &frame { Some(texture) } else { None }) .and_then(|texture| { - texture - .downcast_ref::<::TextureId>() - .cloned() - }) - .unwrap_or_else(|| { - let texture = renderer - .import_memory( - &frame.pixels_rgba, - (frame.width as i32, frame.height as i32).into(), - false, - ) - .expect("Failed to import cursor bitmap"); - pointer_images.push((frame.clone(), Box::new(texture.clone()))); - texture + texture.downcast_ref::::TextureId>>() }); + let pointer_image = match maybe_image { + Some(image) => image, + None => { + let texture = TextureBuffer::from_memory( + renderer, + &frame.pixels_rgba, + (frame.width as i32, frame.height as i32), + false, + integer_scale as i32, + Transform::Normal, + None, + ) + .expect("Failed to import cursor bitmap"); + pointer_images.push((frame.clone(), Box::new(texture.clone()))); + pointer_images + .last() + .and_then(|(_, i)| { + i.downcast_ref::::TextureId>>() + }) + .unwrap() + } + }; + let hotspot = Point::::from((frame.xhot as i32, frame.yhot as i32)).to_f64(); *state.current_image.borrow_mut() = Some(frame); - Some( - PointerElement::new(seat, pointer_image.clone(), location - hotspot, new_frame) - .into(), - ) + return vec![CursorRenderElement::Static( + TextureRenderElement::from_texture_buffer( + (location - hotspot).to_physical(scale), + pointer_image, + None, + None, + ), + )]; } else { - None + Vec::new() } } } diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 5692974e..0ba64e8d 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -6,162 +6,74 @@ use crate::{ state::Fps, utils::prelude::*, }; +//grabs::{MoveGrabRenderElement, SeatMoveGrabState}, use crate::{ - shell::grabs::{MoveGrabRenderElement, SeatMoveGrabState}, - state::Common, - wayland::handlers::data_device::get_dnd_icon, + shell::WorkspaceRenderElement, state::Common, wayland::handlers::data_device::get_dnd_icon, }; -use slog::Logger; use smithay::{ backend::{ drm::DrmNode, renderer::{ - gles2::{Gles2Renderbuffer, Gles2Renderer, Gles2Texture}, - multigpu::{egl::EglGlesBackend, Error as MultiError, MultiFrame, MultiRenderer}, - Frame, ImportAll, Renderer, + damage::{ + DamageTrackedRenderer, DamageTrackedRendererError as RenderError, OutputNoMode, + }, + gles2::{Gles2Renderbuffer, Gles2Renderer}, + multigpu::{egl::EglGlesBackend, MultiFrame, MultiRenderer}, + ImportAll, ImportMem, Renderer, }, }, - desktop::{ - draw_layer_popups, draw_layer_surface, draw_window, draw_window_popups, - layer_map_for_output, - space::{RenderElement, RenderError, SpaceOutputTuple, SurfaceTree}, - utils::damage_from_surface_tree, - Window, - }, output::Output, - utils::{Physical, Point, Rectangle, Scale, Transform}, - wayland::shell::wlr_layer::Layer as WlrLayer, + utils::{Physical, Rectangle}, }; pub mod cursor; -use self::cursor::PointerElement; +use self::cursor::CursorRenderElement; -pub type GlMultiRenderer<'a> = - MultiRenderer<'a, 'a, EglGlesBackend, EglGlesBackend, Gles2Renderbuffer>; -pub type GlMultiFrame = MultiFrame; +pub type GlMultiRenderer<'a> = MultiRenderer< + 'a, + 'a, + EglGlesBackend, + EglGlesBackend, + Gles2Renderbuffer, +>; +pub type GlMultiFrame = MultiFrame, EglGlesBackend>; static CLEAR_COLOR: [f32; 4] = [0.153, 0.161, 0.165, 1.0]; -smithay::custom_elements! { - pub CustomElem<=Gles2Renderer>; - SurfaceTree=SurfaceTree, - PointerElement=PointerElement::, - MoveGrabRenderElement=MoveGrabRenderElement, - #[cfg(feature = "debug")] - EguiFrame=EguiFrame, +smithay::render_elements! { + pub CosmicElement where R: ImportAll; + WorkspaceElement=WorkspaceRenderElement, + CursorElement=CursorRenderElement, + //MoveGrabRenderElement=MoveGrabRenderElement, + //#[cfg(feature = "debug")] + //EguiFrame=EguiFrame, } -// TODO: due to the lifetime of MultiRenderer, we cannot be generic over CustomElem's renderer -// util after GATs land. So we generate with the macro for Gles2 and then -// do a manual impl for MultiRenderer. -impl RenderElement> for CustomElem { - fn id(&self) -> usize { - RenderElement::::id(self) - } - - fn location(&self, scale: impl Into>) -> Point { - RenderElement::::location(self, scale) - } - - fn geometry(&self, scale: impl Into>) -> Rectangle { - RenderElement::::geometry(self, scale) - } - - fn accumulated_damage( - &self, - scale: impl Into>, - for_values: Option>, - ) -> Vec> { - RenderElement::::accumulated_damage(self, scale, for_values) - } - - fn opaque_regions( - &self, - scale: impl Into>, - ) -> Option>> { - RenderElement::::opaque_regions(self, scale) - } - - fn draw( - &self, - renderer: &mut GlMultiRenderer<'_>, - frame: &mut GlMultiFrame, - scale: impl Into>, - location: Point, - damage: &[Rectangle], - log: &Logger, - ) -> Result<(), MultiError> { - RenderElement::::draw( - self, - renderer.as_mut(), - frame.as_mut(), - scale, - location, - damage, - log, - ) - .map_err(MultiError::Render) - } - - fn z_index(&self) -> u8 { - RenderElement::::z_index(self) - } -} - -pub trait AsGles2Renderer { - fn as_gles2(&mut self) -> &mut Gles2Renderer; -} -impl AsGles2Renderer for Gles2Renderer { - fn as_gles2(&mut self) -> &mut Gles2Renderer { - self - } -} -impl AsGles2Renderer for GlMultiRenderer<'_> { - fn as_gles2(&mut self) -> &mut Gles2Renderer { - self.as_mut() - } -} - -pub fn needs_buffer_reset(output: &Output, state: &Common) -> bool { - use std::sync::atomic::{AtomicBool, Ordering}; - struct DidCustomRendering(AtomicBool); - - let will_render_custom = { - let workspace = state.shell.active_space(output); - workspace.get_fullscreen(output).is_some() - }; - - let userdata = output.user_data(); - userdata.insert_if_missing(|| DidCustomRendering(AtomicBool::new(false))); - userdata - .get::() - .unwrap() - .0 - .swap(will_render_custom, Ordering::AcqRel) - != will_render_custom -} - -pub fn cursor_custom_elements( +pub fn cursor_elements( renderer: &mut R, state: &Common, output: &Output, hardware_cursor: bool, -) -> Vec +) -> Vec where - R: AsGles2Renderer, + R: Renderer + ImportAll + ImportMem, + ::TextureId: Clone + 'static, + E: From>, { - let mut custom_elements = Vec::new(); + let scale = output.current_scale().fractional_scale(); + let mut elements = Vec::new(); - for seat in &state.seats { + for seat in state.seats() { let pointer = match seat.get_pointer() { Some(ptr) => ptr, None => continue, }; let location = state .shell - .space_relative_output_geometry(pointer.current_location().to_i32_round(), output); + .map_global_to_space(pointer.current_location().to_i32_round(), output); + /* if let Some(grab) = seat .user_data() .get::() @@ -172,147 +84,92 @@ where { custom_elements.push(grab); } + */ if let Some(wl_surface) = get_dnd_icon(seat) { - custom_elements.push(cursor::draw_dnd_icon(wl_surface, location.to_i32_round()).into()); + elements.extend( + cursor::draw_dnd_icon(&wl_surface, location.to_i32_round(), scale) + .into_iter() + .map(E::from), + ); } - if let Some(cursor) = cursor::draw_cursor( - renderer.as_gles2(), - seat, - location, - &state.start_time, - !hardware_cursor, - ) { - custom_elements.push(cursor) - } + elements.extend( + cursor::draw_cursor( + renderer, + seat, + location, + scale.into(), + &state.start_time, + !hardware_cursor, + ) + .into_iter() + .map(E::from), + ); } - custom_elements + elements } pub fn render_output( gpu: Option<&DrmNode>, renderer: &mut R, - age: u8, + damage_tracker: &mut DamageTrackedRenderer, + age: usize, state: &mut Common, output: &Output, hardware_cursor: bool, #[cfg(feature = "debug")] mut fps: Option<&mut Fps>, ) -> Result>>, RenderError> where - R: Renderer + ImportAll + AsGles2Renderer, + R: Renderer + ImportAll + ImportMem, ::TextureId: Clone + 'static, - CustomElem: RenderElement, { - let workspace = state.shell.active_space(output).idx; + let idx = state.shell.workspaces.active_num(output); render_workspace( gpu, renderer, + damage_tracker, age, state, - workspace, output, + idx, hardware_cursor, ) } pub fn render_workspace( - gpu: Option<&DrmNode>, + _gpu: Option<&DrmNode>, renderer: &mut R, - age: u8, + damage_tracker: &mut DamageTrackedRenderer, + age: usize, state: &mut Common, - space_idx: u8, output: &Output, + idx: usize, hardware_cursor: bool, #[cfg(feature = "debug")] mut fps: Option<&mut Fps>, ) -> Result>>, RenderError> where - R: Renderer + ImportAll + AsGles2Renderer, + R: Renderer + ImportAll + ImportMem, ::TextureId: Clone + 'static, - CustomElem: RenderElement, { #[cfg(feature = "debug")] if let Some(ref mut fps) = fps { fps.start(); } - let space_idx = space_idx as usize; - let workspace = &mut state.shell.spaces[space_idx]; - let maybe_fullscreen_window = workspace.get_fullscreen(output).cloned(); + let workspace = &state + .shell + .workspaces + .get(idx, output) + .ok_or(OutputNoMode)?; - let res = if let Some(window) = maybe_fullscreen_window { - #[cfg(not(feature = "debug"))] - { - render_fullscreen(gpu, renderer, window, state, output, hardware_cursor) - } - #[cfg(feature = "debug")] - { - render_fullscreen( - gpu, - renderer, - window, - state, - output, - hardware_cursor, - fps.as_deref_mut(), - ) - } - } else { - #[cfg(not(feature = "debug"))] - { - render_desktop( - gpu, - renderer, - age, - state, - space_idx, - output, - hardware_cursor, - ) - } - #[cfg(feature = "debug")] - { - render_desktop( - gpu, - renderer, - age, - state, - space_idx, - output, - hardware_cursor, - fps.as_deref_mut(), - ) - } - }; - - #[cfg(feature = "debug")] - if let Some(ref mut fps) = fps { - fps.end(); - } - - res -} - -fn render_desktop( - _gpu: Option<&DrmNode>, - renderer: &mut R, - age: u8, - state: &mut Common, - space_idx: usize, - output: &Output, - hardware_cursor: bool, - #[cfg(feature = "debug")] fps: Option<&mut Fps>, -) -> Result>>, RenderError> -where - R: Renderer + ImportAll + AsGles2Renderer, - ::TextureId: Clone + 'static, - CustomElem: RenderElement, -{ - let mut custom_elements = Vec::::new(); + let mut elements: Vec> = + cursor_elements(renderer, state, output, hardware_cursor); #[cfg(feature = "debug")] { + // TODO add debug elements let workspace = &state.shell.spaces[space_idx]; let output_geo = workspace .space @@ -346,132 +203,20 @@ where } } - custom_elements.extend(cursor_custom_elements( - renderer, - state, - output, - hardware_cursor, - )); + elements.extend( + workspace + .render_output(output) + .map_err(|_| OutputNoMode)? + .into_iter() + .map(Into::into), + ); - state.shell.spaces[space_idx].space.render_output( - renderer, - &output, - age as usize, - CLEAR_COLOR, - &*custom_elements, - ) -} - -fn render_fullscreen( - _gpu: Option<&DrmNode>, - renderer: &mut R, - window: Window, - state: &mut Common, - output: &Output, - hardware_cursor: bool, - #[cfg(feature = "debug")] fps: Option<&mut Fps>, -) -> Result>>, RenderError> -where - R: Renderer + ImportAll + AsGles2Renderer, - ::TextureId: Clone + 'static, - CustomElem: RenderElement, -{ - let transform = Transform::from(output.current_transform()); - let mode = output.current_mode().unwrap(); - let scale = output.current_scale().fractional_scale(); - - let mut custom_elements = Vec::::new(); + let res = damage_tracker.render_output(renderer, age, &elements, CLEAR_COLOR, None); #[cfg(feature = "debug")] - if let Some(fps) = fps { - let output_geo = output.geometry(); - let fps_overlay = fps_ui( - _gpu, - state, - fps, - Rectangle::from_loc_and_size((0, 0), output_geo.size) - .to_f64() - .to_physical(scale), - scale, - ); - custom_elements.push(fps_overlay.into()); + if let Some(ref mut fps) = fps { + fps.end(); } - custom_elements.extend(cursor_custom_elements( - renderer, - state, - output, - hardware_cursor, - )); - - renderer - .render(mode.size, transform, |renderer, frame| { - let mut damage = window.accumulated_damage((0.0, 0.0), scale, None); - frame.clear( - CLEAR_COLOR, - &[Rectangle::from_loc_and_size((0, 0), mode.size)], - )?; - draw_window( - renderer, - frame, - &window, - scale, - (0.0, 0.0), - &[Rectangle::from_loc_and_size((0, 0), mode.size)], - &slog_scope::logger(), - )?; - draw_window_popups( - renderer, - frame, - &window, - scale, - (0.0, 0.0), - &[Rectangle::from_loc_and_size((0, 0), mode.size)], - &slog_scope::logger(), - )?; - let layer_map = layer_map_for_output(output); - for layer_surface in layer_map.layers_on(WlrLayer::Overlay) { - let geo = layer_map.layer_geometry(&layer_surface).unwrap(); - draw_layer_surface( - renderer, - frame, - layer_surface, - scale, - geo.loc.to_f64().to_physical(scale), - &[Rectangle::from_loc_and_size( - (0, 0), - geo.size.to_physical_precise_round(scale), - )], - &slog_scope::logger(), - )?; - draw_layer_popups( - renderer, - frame, - layer_surface, - scale, - geo.loc.to_f64().to_physical(scale), - &[Rectangle::from_loc_and_size( - (0, 0), - geo.size.to_physical_precise_round(scale), - )], - &slog_scope::logger(), - )?; - damage.extend(damage_from_surface_tree( - layer_surface.wl_surface(), - geo.loc.to_f64().to_physical(scale), - scale, - None, - )); - } - for elem in custom_elements { - let loc = elem.location(scale); - let geo = elem.geometry(scale); - let elem_damage = elem.accumulated_damage(scale, None); - elem.draw(renderer, frame, scale, loc, &[geo], &slog_scope::logger())?; - damage.extend(elem_damage) - } - Ok(Some(damage)) - }) - .and_then(std::convert::identity) - .map_err(RenderError::::Rendering) + res } diff --git a/src/backend/winit.rs b/src/backend/winit.rs index b6a67ed1..8456f0ac 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -10,7 +10,7 @@ use crate::{ use anyhow::{anyhow, Context, Result}; use smithay::{ backend::{ - renderer::{ImportDma, ImportEgl}, + renderer::{damage::DamageTrackedRenderer, ImportDma, ImportEgl}, winit::{self, WinitEvent, WinitGraphicsBackend, WinitVirtualDevice}, }, desktop::layer_map_for_output, @@ -30,31 +30,23 @@ pub struct WinitState { // The winit backend currently has no notion of multiple windows pub backend: WinitGraphicsBackend, output: Output, - age_reset: u8, + damage_tracker: DamageTrackedRenderer, #[cfg(feature = "debug")] fps: Fps, } impl WinitState { pub fn render_output(&mut self, state: &mut Common) -> Result<()> { - if render::needs_buffer_reset(&self.output, state) { - self.reset_buffers(); - } - self.backend .bind() .with_context(|| "Failed to bind buffer")?; - let age = if self.age_reset > 0 { - self.age_reset -= 1; - 0 - } else { - self.backend.buffer_age().unwrap_or(0) - }; + let age = self.backend.buffer_age().unwrap_or(0); match render::render_output( None, self.backend.renderer(), - age as u8, + &mut self.damage_tracker, + age, state, &self.output, true, @@ -62,11 +54,7 @@ impl WinitState { Some(&mut self.fps), ) { Ok(damage) => { - state - .shell - .active_space_mut(&self.output) - .space - .send_frames(state.start_time.elapsed().as_millis() as u32); + state.send_frames(&self.output); self.backend .submit(damage.as_ref().map(|x| &**x)) .with_context(|| "Failed to submit buffer for display")?; @@ -104,10 +92,6 @@ impl WinitState { Ok(()) } } - - pub fn reset_buffers(&mut self) { - self.age_reset = 3; - } } pub fn init_backend( @@ -180,8 +164,7 @@ pub fn init_backend( .handle() .insert_source(event_source, move |_, _, data| { match input.dispatch_new_events(|event| { - data.state - .process_winit_event(&data.display.handle(), event, &render_ping_handle) + data.state.process_winit_event(event, &render_ping_handle) }) { Ok(_) => { event_ping_handle.ping(); @@ -202,9 +185,9 @@ pub fn init_backend( state.backend = BackendData::Winit(WinitState { backend, output: output.clone(), + damage_tracker: DamageTrackedRenderer::from_output(&output), #[cfg(feature = "debug")] fps: Fps::default(), - age_reset: 0, }); state .common @@ -250,19 +233,14 @@ fn init_egl_client_side( } impl State { - pub fn process_winit_event( - &mut self, - dh: &DisplayHandle, - event: WinitEvent, - render_ping: &ping::Ping, - ) { + pub fn process_winit_event(&mut self, event: WinitEvent, render_ping: &ping::Ping) { // here we can handle special cases for winit inputs match event { WinitEvent::Focus(true) => { - for seat in self.common.seats.clone().iter() { + for seat in self.common.seats().cloned().collect::>().iter() { let devices = seat.user_data().get::().unwrap(); if devices.has_device(&WinitVirtualDevice) { - set_active_output(seat, &self.backend.winit().output); + seat.set_active_output(&self.backend.winit().output); break; } } @@ -286,7 +264,7 @@ impl State { output.delete_mode(output.current_mode().unwrap()); output.set_preferred(mode); output.change_current_state(Some(mode), None, None, None); - layer_map_for_output(output).arrange(dh); + layer_map_for_output(output).arrange(); self.common.output_configuration_state.update(); self.common.shell.refresh_outputs(); render_ping.ping(); diff --git a/src/backend/x11.rs b/src/backend/x11.rs index 53c4c1ca..fbe8f6f6 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -13,7 +13,9 @@ use smithay::{ allocator::dmabuf::Dmabuf, egl::{EGLContext, EGLDisplay}, input::{Event, InputEvent}, - renderer::{gles2::Gles2Renderer, Bind, ImportDma, ImportEgl}, + renderer::{ + damage::DamageTrackedRenderer, gles2::Gles2Renderer, Bind, ImportDma, ImportEgl, + }, x11::{Window, WindowBuilder, X11Backend, X11Event, X11Handle, X11Input, X11Surface}, }, desktop::layer_map_for_output, @@ -114,6 +116,7 @@ impl X11State { self.surfaces.push(Surface { window, surface, + damage_tracker: DamageTrackedRenderer::from_output(&output), output: output.clone(), render: ping.clone(), dirty: false, @@ -168,6 +171,7 @@ impl X11State { pub struct Surface { window: Window, + damage_tracker: DamageTrackedRenderer, surface: X11Surface, output: Output, render: ping::Ping, @@ -183,10 +187,6 @@ impl Surface { renderer: &mut Gles2Renderer, state: &mut Common, ) -> Result<()> { - if render::needs_buffer_reset(&self.output, state) { - self.surface.reset_buffers(); - } - let (buffer, age) = self .surface .buffer() @@ -198,7 +198,8 @@ impl Surface { match render::render_output( None, renderer, - age as u8, + &mut self.damage_tracker, + age as usize, state, &self.output, true, @@ -206,11 +207,7 @@ impl Surface { Some(&mut self.fps), ) { Ok(_) => { - state - .shell - .active_space_mut(&self.output) - .space - .send_frames(state.start_time.elapsed().as_millis() as u32); + state.send_frames(&self.output); self.surface .submit() .with_context(|| "Failed to submit buffer for display")?; @@ -336,7 +333,7 @@ pub fn init_backend( output.delete_mode(output.current_mode().unwrap()); output.change_current_state(Some(mode), None, None, None); output.set_preferred(mode); - layer_map_for_output(output).arrange(&data.display.handle()); + layer_map_for_output(output).arrange(); data.state.common.output_configuration_state.update(); data.state.common.shell.refresh_outputs(); surface.dirty = true; @@ -405,10 +402,10 @@ impl State { .unwrap(); let device = event.device(); - for seat in self.common.seats.clone().iter() { + for seat in self.common.seats().cloned().collect::>().iter() { let devices = seat.user_data().get::().unwrap(); if devices.has_device(&device) { - set_active_output(seat, &output); + seat.set_active_output(&output); break; } } diff --git a/src/config/mod.rs b/src/config/mod.rs index 3a225ea1..401e24e8 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - shell::{focus::FocusDirection, Shell}, + shell::{focus::FocusDirection, Shell, WorkspaceAmount}, state::{BackendData, Data}, }; use serde::{Deserialize, Serialize}; @@ -32,6 +32,7 @@ pub struct Config { pub struct StaticConfig { pub key_bindings: HashMap, pub workspace_mode: WorkspaceMode, + pub workspace_amount: WorkspaceAmount, pub floating_default: bool, } @@ -215,6 +216,7 @@ impl Config { StaticConfig { key_bindings: HashMap::new(), workspace_mode: WorkspaceMode::Global, + workspace_amount: WorkspaceAmount::Dynamic, floating_default: false, } } @@ -787,6 +789,6 @@ pub enum Action { ToggleTiling, ToggleWindowFloating, Fullscreen, - Screenshot, + //Screenshot, Spawn(String), } diff --git a/src/input/mod.rs b/src/input/mod.rs index b599f882..d22c64ae 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -2,7 +2,7 @@ use crate::{ config::{Action, Config}, - shell::{grabs::SeatMoveGrabState, Workspace}, + shell::{focus::target::PointerFocusTarget, Workspace}, // shell::grabs::SeatMoveGrabState state::Common, utils::prelude::*, }; @@ -11,17 +11,17 @@ use smithay::{ AbsolutePositionEvent, Axis, AxisSource, Device, DeviceCapability, InputBackend, InputEvent, KeyState, PointerAxisEvent, }, - desktop::{layer_map_for_output, Kind, WindowSurfaceType}, + desktop::layer_map_for_output, input::{ keyboard::{keysyms, FilterResult, KeysymHandle, XkbConfig}, pointer::{AxisFrame, ButtonEvent, CursorImageStatus, MotionEvent}, Seat, SeatState, }, output::Output, - reexports::wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle}, - utils::{Buffer, Logical, Point, Rectangle, Size, SERIAL_COUNTER}, + reexports::wayland_server::DisplayHandle, + utils::{Logical, Point, Rectangle, SERIAL_COUNTER}, wayland::{ - keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat, + keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat, seat::WaylandFocus, shell::wlr_layer::Layer as WlrLayer, }, }; @@ -103,6 +103,7 @@ impl Devices { pub fn add_seat( dh: &DisplayHandle, seat_state: &mut SeatState, + output: &Output, config: &Config, name: String, ) -> Seat { @@ -111,7 +112,8 @@ pub fn add_seat( userdata.insert_if_missing(SeatId::default); userdata.insert_if_missing(Devices::default); userdata.insert_if_missing(SupressedKeys::default); - userdata.insert_if_missing(SeatMoveGrabState::default); + //userdata.insert_if_missing(SeatMoveGrabState::default); + userdata.insert_if_missing(|| ActiveOutput(RefCell::new(output.clone()))); userdata.insert_if_missing(|| RefCell::new(CursorImageStatus::Default)); // A lot of clients bind keyboard and pointer unconditionally once on launch.. @@ -143,7 +145,7 @@ impl State { match event { InputEvent::DeviceAdded { device } => { - let seat = &mut self.common.last_active_seat; + let seat = &mut self.common.last_active_seat(); let userdata = seat.user_data(); let devices = userdata.get::().unwrap(); for cap in devices.add_device(&device) { @@ -159,7 +161,7 @@ impl State { } } InputEvent::DeviceRemoved { device } => { - for seat in &mut self.common.seats { + for seat in &mut self.common.seats() { let userdata = seat.user_data(); let devices = userdata.get::().unwrap(); if devices.has_device(&device) { @@ -182,16 +184,17 @@ impl State { use smithay::backend::input::KeyboardKeyEvent; let device = event.device(); - for seat in self.common.seats.clone().iter() { - let current_output = active_output(seat, &self.common); + for seat in self.common.seats().cloned().collect::>().iter() { + let current_output = seat.active_output(); let workspace = self.common.shell.active_space_mut(¤t_output); let shortcuts_inhibited = workspace - .focus_stack(seat) + .focus_stack + .get(seat) .last() .and_then(|window| { - seat.keyboard_shortcuts_inhibitor_for_surface( - &window.toplevel().wl_surface(), - ) + window.wl_surface().and_then(|surface| { + seat.keyboard_shortcuts_inhibitor_for_surface(&surface) + }) }) .map(|inhibitor| inhibitor.is_active()) .unwrap_or(false); @@ -309,34 +312,31 @@ impl State { slog_scope::info!("Debug overlay not included in this version") } Action::Close => { - let current_output = active_output(seat, &self.common); + let current_output = seat.active_output(); let workspace = self.common.shell.active_space_mut(¤t_output); - if let Some(window) = workspace.focus_stack(seat).last() { - #[allow(irrefutable_let_patterns)] - if let Kind::Xdg(xdg) = &window.toplevel() { - xdg.send_close(); - } + if let Some(window) = workspace.focus_stack.get(seat).last() { + window.send_close(); } } Action::Workspace(key_num) => { - let current_output = active_output(seat, &self.common); + let current_output = seat.active_output(); let workspace = match key_num { 0 => 9, x => x - 1, }; - if let Some(motion_event) = self.common.shell.activate( - seat, - ¤t_output, - workspace as usize, - ) { + if let Some(motion_event) = self + .common + .shell + .activate(¤t_output, workspace as usize) + { if let Some(ptr) = seat.get_pointer() { ptr.motion(self, None, &motion_event); } } } Action::MoveToWorkspace(key_num) => { - let current_output = active_output(seat, &self.common); + let current_output = seat.active_output(); let workspace = match key_num { 0 => 9, x => x - 1, @@ -348,52 +348,47 @@ impl State { ); } Action::Focus(focus) => { - let current_output = active_output(seat, &self.common); + let current_output = seat.active_output(); let workspace = self.common.shell.active_space_mut(¤t_output); - let focus_stack = workspace.focus_stack(seat); - if let Some(window) = workspace.tiling_layer.move_focus( + let focus_stack = workspace.focus_stack.get(seat); + if let Some(target) = workspace.tiling_layer.next_focus( focus, seat, - &mut workspace.space, focus_stack.iter(), ) { std::mem::drop(focus_stack); - Common::set_focus( - self, - Some(window.toplevel().wl_surface()), - seat, - None, - ); + Common::set_focus(self, Some(&target), seat, None); } } Action::Fullscreen => { - let current_output = active_output(seat, &self.common); + let current_output = seat.active_output(); let workspace = self.common.shell.active_space_mut(¤t_output); - let focused_window = workspace.focus_stack(seat).last(); - if let Some(window) = focused_window { + let focus_stack = workspace.focus_stack.get(seat); + let focused_window = focus_stack.last(); + if let Some(window) = focused_window.map(|f| f.active_window()) + { workspace.fullscreen_toggle(&window, ¤t_output); } } Action::Orientation(orientation) => { - let output = active_output(seat, &self.common); + let output = seat.active_output(); let workspace = self.common.shell.active_space_mut(&output); - let focus_stack = workspace.focus_stack(seat); + let focus_stack = workspace.focus_stack.get(seat); workspace.tiling_layer.update_orientation( orientation, &seat, - &mut workspace.space, focus_stack.iter(), ); } Action::ToggleTiling => { - let output = active_output(seat, &self.common); + let output = seat.active_output(); let workspace = self.common.shell.active_space_mut(&output); workspace.toggle_tiling(seat); } Action::ToggleWindowFloating => { - let output = active_output(seat, &self.common); + let output = seat.active_output(); let workspace = self.common.shell.active_space_mut(&output); workspace.toggle_floating_window(seat); } @@ -407,80 +402,81 @@ impl State { { slog_scope::warn!("Failed to spawn: {}", err); } - } - Action::Screenshot => { - let home = match std::env::var("HOME") { - Ok(home) => home, - Err(err) => { - slog_scope::error!( - "$HOME is not set, can't save screenshots: {}", - err - ); - break; - } - }; - let timestamp = match std::time::SystemTime::UNIX_EPOCH - .elapsed() - { - Ok(duration) => duration.as_secs(), - Err(err) => { - slog_scope::error!("Unable to get timestamp, can't save screenshots: {}", err); - break; - } - }; - for output in self.common.shell.outputs.clone().into_iter() { - match self - .backend - .offscreen_for_output(&output, &mut self.common) - { - Ok((buffer, size)) => { - let mut path = std::path::PathBuf::new(); - path.push(&home); - path.push(format!( - "{}_{}.png", - output.name(), - timestamp - )); + } /* + Action::Screenshot => { + let home = match std::env::var("HOME") { + Ok(home) => home, + Err(err) => { + slog_scope::error!( + "$HOME is not set, can't save screenshots: {}", + err + ); + break; + } + }; + let timestamp = match std::time::SystemTime::UNIX_EPOCH + .elapsed() + { + Ok(duration) => duration.as_secs(), + Err(err) => { + slog_scope::error!("Unable to get timestamp, can't save screenshots: {}", err); + break; + } + }; + for output in self.common.shell.outputs.clone().into_iter() { + match self + .backend + .offscreen_for_output(&output, &mut self.common) + { + Ok((buffer, size)) => { + let mut path = std::path::PathBuf::new(); + path.push(&home); + path.push(format!( + "{}_{}.png", + output.name(), + timestamp + )); - fn write_png( - path: impl AsRef, - data: Vec, - size: Size, - ) -> anyhow::Result<()> - { - use std::{fs, io}; + fn write_png( + path: impl AsRef, + data: Vec, + size: Size, + ) -> anyhow::Result<()> + { + use std::{fs, io}; - let file = io::BufWriter::new( - fs::File::create(&path)?, - ); - let mut encoder = png::Encoder::new( - file, - size.w as u32, - size.h as u32, - ); - encoder.set_color(png::ColorType::Rgba); - encoder.set_depth(png::BitDepth::Eight); - let mut writer = encoder.write_header()?; - writer.write_image_data(&data)?; - Ok(()) - } + let file = io::BufWriter::new( + fs::File::create(&path)?, + ); + let mut encoder = png::Encoder::new( + file, + size.w as u32, + size.h as u32, + ); + encoder.set_color(png::ColorType::Rgba); + encoder.set_depth(png::BitDepth::Eight); + let mut writer = encoder.write_header()?; + writer.write_image_data(&data)?; + Ok(()) + } - if let Err(err) = write_png(&path, buffer, size) { - slog_scope::error!( - "Unable to save screenshot at {}: {}", - path.display(), - err - ); - } - } - Err(err) => slog_scope::error!( - "Could not save screenshot for output {}: {}", - output.name(), - err - ), - } - } - } + if let Err(err) = write_png(&path, buffer, size) { + slog_scope::error!( + "Unable to save screenshot at {}: {}", + path.display(), + err + ); + } + } + Err(err) => slog_scope::error!( + "Could not save screenshot for output {}: {}", + output.name(), + err + ), + } + } + } + */ } } break; @@ -491,11 +487,11 @@ impl State { use smithay::backend::input::PointerMotionEvent; let device = event.device(); - for seat in self.common.seats.clone().iter() { + for seat in self.common.seats().cloned().collect::>().iter() { let userdata = seat.user_data(); let devices = userdata.get::().unwrap(); if devices.has_device(&device) { - let current_output = active_output(seat, &self.common); + let current_output = seat.active_output(); let mut position = seat.get_pointer().unwrap().current_location(); position += event.delta(); @@ -508,7 +504,7 @@ impl State { .cloned() .unwrap_or(current_output.clone()); if output != current_output { - set_active_output(seat, &output); + seat.set_active_output(&output); } let output_geometry = output.geometry(); @@ -520,10 +516,7 @@ impl State { .min((output_geometry.loc.y + output_geometry.size.h) as f64); let serial = SERIAL_COUNTER.next_serial(); - let relative_pos = self - .common - .shell - .space_relative_output_geometry(position, &output); + let relative_pos = self.common.shell.map_global_to_space(position, &output); let workspace = self.common.shell.active_space_mut(&output); let under = State::surface_under( position, @@ -559,18 +552,15 @@ impl State { } InputEvent::PointerMotionAbsolute { event, .. } => { let device = event.device(); - for seat in self.common.seats.clone().iter() { + for seat in self.common.seats().cloned().collect::>().iter() { let userdata = seat.user_data(); let devices = userdata.get::().unwrap(); if devices.has_device(&device) { - let output = active_output(seat, &self.common); + let output = seat.active_output(); let geometry = output.geometry(); let position = geometry.loc.to_f64() + event.position_transformed(geometry.size); - let relative_pos = self - .common - .shell - .space_relative_output_geometry(position, &output); + let relative_pos = self.common.shell.map_global_to_space(position, &output); let workspace = self.common.shell.active_space_mut(&output); let serial = SERIAL_COUNTER.next_serial(); let under = State::surface_under( @@ -609,7 +599,7 @@ impl State { use smithay::backend::input::{ButtonState, PointerButtonEvent}; let device = event.device(); - for seat in self.common.seats.clone().iter() { + for seat in self.common.seats().cloned().collect::>().iter() { let userdata = seat.user_data(); let devices = userdata.get::().unwrap(); if devices.has_device(&device) { @@ -649,13 +639,10 @@ impl State { if !seat.get_pointer().unwrap().is_grabbed() && !seat.get_keyboard().map(|k| k.is_grabbed()).unwrap_or(false) { - let output = active_output(seat, &self.common); + let output = seat.active_output(); let pos = seat.get_pointer().unwrap().current_location(); - let output_geo = output.geometry(); - let relative_pos = self - .common - .shell - .space_relative_output_geometry(pos, &output); + let relative_pos = + self.common.shell.map_global_to_space(pos, &output); let workspace = self.common.shell.active_space_mut(&output); let layers = layer_map_for_output(&output); let mut under = None; @@ -665,23 +652,10 @@ impl State { layers.layer_under(WlrLayer::Overlay, relative_pos) { if layer.can_receive_keyboard_focus() { - let layer_loc = - layers.layer_geometry(layer).unwrap().loc; - under = layer - .surface_under( - pos - output_geo.loc.to_f64() - - layer_loc.to_f64(), - WindowSurfaceType::ALL, - ) - .map(|(_, _)| layer.wl_surface().clone()); + under = Some(layer.clone().into()); } } else { - under = window - .surface_under( - pos - output_geo.loc.to_f64(), - WindowSurfaceType::ALL, - ) - .map(|(_, _)| window.toplevel().wl_surface().clone()); + under = Some(window.clone().into()); } } else { if let Some(layer) = layers @@ -689,35 +663,18 @@ impl State { .or_else(|| layers.layer_under(WlrLayer::Top, relative_pos)) { if layer.can_receive_keyboard_focus() { - let layer_loc = - layers.layer_geometry(layer).unwrap().loc; - under = layer - .surface_under( - pos - output_geo.loc.to_f64() - - layer_loc.to_f64(), - WindowSurfaceType::ALL, - ) - .map(|(_, _)| layer.wl_surface().clone()); + under = Some(layer.clone().into()); } - } else if let Some((window, _, _)) = workspace - .space - .surface_under(relative_pos, WindowSurfaceType::ALL) + } else if let Some((window, _)) = + workspace.element_under(relative_pos) { - under = Some(window.toplevel().wl_surface().clone()); + under = Some(window.clone().into()); } else if let Some(layer) = layers .layer_under(WlrLayer::Bottom, pos) .or_else(|| layers.layer_under(WlrLayer::Background, pos)) { if layer.can_receive_keyboard_focus() { - let layer_loc = - layers.layer_geometry(layer).unwrap().loc; - under = layer - .surface_under( - pos - output_geo.loc.to_f64() - - layer_loc.to_f64(), - WindowSurfaceType::ALL, - ) - .map(|(_, _)| layer.wl_surface().clone()); + under = Some(layer.clone().into()); } }; } @@ -740,7 +697,7 @@ impl State { } InputEvent::PointerAxis { event, .. } => { let device = event.device(); - for seat in self.common.seats.clone().iter() { + for seat in self.common.seats().cloned().collect::>().iter() { #[cfg(feature = "debug")] if self.common.seats.iter().position(|x| x == seat).unwrap() == 0 && self.common.egui.active @@ -820,7 +777,7 @@ impl State { output: &Output, output_geo: Rectangle, workspace: &Workspace, - ) -> Option<(WlSurface, Point)> { + ) -> Option<(PointerFocusTarget, Point)> { let layers = layer_map_for_output(output); if let Some(window) = workspace.get_fullscreen(output) { if let Some(layer) = layers @@ -828,16 +785,9 @@ impl State { .or_else(|| layers.layer_under(WlrLayer::Top, relative_pos)) { let layer_loc = layers.layer_geometry(layer).unwrap().loc; - layer - .surface_under( - global_pos - output_geo.loc.to_f64() - layer_loc.to_f64(), - WindowSurfaceType::ALL, - ) - .map(|(s, loc)| (s, loc + layer_loc + output_geo.loc)) + Some((layer.clone().into(), output_geo.loc + layer_loc)) } else { - window - .surface_under(global_pos - output_geo.loc.to_f64(), WindowSurfaceType::ALL) - .map(|(s, loc)| (s, loc + output_geo.loc)) + Some((window.clone().into(), output_geo.loc)) } } else { if let Some(layer) = layers @@ -845,28 +795,18 @@ impl State { .or_else(|| layers.layer_under(WlrLayer::Top, relative_pos)) { let layer_loc = layers.layer_geometry(layer).unwrap().loc; - layer - .surface_under( - global_pos - output_geo.loc.to_f64() - layer_loc.to_f64(), - WindowSurfaceType::ALL, - ) - .map(|(s, loc)| (s, loc + layer_loc + output_geo.loc)) - } else if let Some((_, surface, loc)) = workspace - .space - .surface_under(relative_pos, WindowSurfaceType::ALL) - { - Some((surface, loc + (global_pos - relative_pos).to_i32_round())) + Some((layer.clone().into(), output_geo.loc + layer_loc)) + } else if let Some((mapped, loc)) = workspace.element_under(relative_pos) { + Some(( + mapped.clone().into(), + loc + (global_pos - relative_pos).to_i32_round(), + )) } else if let Some(layer) = layers .layer_under(WlrLayer::Bottom, relative_pos) .or_else(|| layers.layer_under(WlrLayer::Background, relative_pos)) { let layer_loc = layers.layer_geometry(layer).unwrap().loc; - layer - .surface_under( - global_pos - output_geo.loc.to_f64() - layer_loc.to_f64(), - WindowSurfaceType::ALL, - ) - .map(|(s, loc)| (s, loc + layer_loc + output_geo.loc)) + Some((layer.clone().into(), output_geo.loc + layer_loc)) } else { None } diff --git a/src/main.rs b/src/main.rs index 4231f427..a96f3d06 100644 --- a/src/main.rs +++ b/src/main.rs @@ -68,7 +68,7 @@ fn main() -> Result<()> { } // trigger routines - data.state.common.shell.refresh(&data.display.handle()); + data.state.common.shell.refresh(); state::Common::refresh_focus(&mut data.state); // do we need to trigger another render diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs new file mode 100644 index 00000000..20096d06 --- /dev/null +++ b/src/shell/element/mod.rs @@ -0,0 +1,631 @@ +use crate::state::State; +use id_tree::NodeId; +use smithay::{ + backend::{ + input::KeyState, + renderer::{element::AsRenderElements, ImportAll, Renderer}, + }, + desktop::{space::SpaceElement, Kind, PopupManager, Window, WindowSurfaceType}, + input::{ + keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, + pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget}, + Seat, + }, + output::Output, + reexports::{ + wayland_protocols::xdg::shell::server::xdg_toplevel::State as XdgState, + wayland_server::{backend::ObjectId, protocol::wl_surface::WlSurface}, + }, + render_elements, space_elements, + utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size}, + wayland::{ + compositor::{with_states, with_surface_tree_downward, TraversalAction}, + seat::WaylandFocus, + shell::xdg::XdgToplevelSurfaceRoleAttributes, + }, +}; +use std::{ + hash::Hash, + sync::{Arc, Mutex}, +}; + +pub mod stack; +pub use self::stack::CosmicStack; +pub mod window; +pub use self::window::CosmicWindow; + +use super::focus::FocusDirection; + +space_elements! { + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + CosmicMappedInternal; + Window=CosmicWindow, + Stack=CosmicStack, +} + +#[derive(Debug, Clone)] +pub struct CosmicMapped { + element: CosmicMappedInternal, + + // associated data + + //tiling + pub(super) tiling_node_id: Arc>>, + //floating + pub(super) last_geometry: Arc>>>, +} + +impl PartialEq for CosmicMapped { + fn eq(&self, other: &Self) -> bool { + self.element == other.element + } +} + +impl Eq for CosmicMapped {} + +impl Hash for CosmicMapped { + fn hash(&self, state: &mut H) { + self.element.hash(state) + } +} + +impl CosmicMapped { + pub fn windows(&self) -> impl Iterator)> + '_ { + match &self.element { + CosmicMappedInternal::Stack(stack) => Box::new(stack.windows().map(|w| { + ( + w, + stack + .header + .lock() + .unwrap() + .as_ref() + .map(|header| Point::from((0, header.height() as i32))) + .unwrap_or(Point::from((0, 0))), + ) + })) + as Box)>>, + CosmicMappedInternal::Window(window) => Box::new(std::iter::once(( + window.window.clone(), + window + .header + .lock() + .unwrap() + .as_ref() + .map(|header| Point::from((0, header.height() as i32))) + .unwrap_or(Point::from((0, 0))), + ))), + _ => Box::new(std::iter::empty()), + } + } + + pub fn active_window(&self) -> Window { + match &self.element { + CosmicMappedInternal::Stack(stack) => stack.active(), + CosmicMappedInternal::Window(win) => win.window.clone(), + _ => unreachable!(), + } + } + + pub fn focus_window(&self, window: &Window) { + match &self.element { + CosmicMappedInternal::Stack(stack) => stack.set_active(window), + _ => {} + } + } + + pub fn has_surface(&self, surface: &WlSurface, surface_type: WindowSurfaceType) -> bool { + self.windows().any(|(w, _)| { + let toplevel = w.toplevel().wl_surface(); + + if surface_type.contains(WindowSurfaceType::TOPLEVEL) { + if toplevel == surface { + return true; + } + } + + if surface_type.contains(WindowSurfaceType::SUBSURFACE) { + use std::sync::atomic::{AtomicBool, Ordering}; + + let found = AtomicBool::new(false); + with_surface_tree_downward( + toplevel, + surface, + |_, _, search| TraversalAction::DoChildren(search), + |s, _, search| { + found.fetch_or(s == *search, Ordering::SeqCst); + }, + |_, _, _| !found.load(Ordering::SeqCst), + ); + if found.load(Ordering::SeqCst) { + return true; + } + } + + if surface_type.contains(WindowSurfaceType::POPUP) { + PopupManager::popups_for_surface(toplevel).any(|(p, _)| p.wl_surface() == surface) + } else { + false + } + }) + } + + pub fn handle_focus(&self, direction: FocusDirection) -> bool { + if let CosmicMappedInternal::Stack(stack) = &self.element { + //TODO: stack.handle_focus(direction) + false + } else { + false + } + } + + pub fn set_tiled(&self, tiled: bool) { + for toplevel in match &self.element { + // we use the tiled state of stack windows anyway to get rid of decorations + CosmicMappedInternal::Stack(s) => None, + CosmicMappedInternal::Window(w) => Some(w.window.toplevel()), + _ => unreachable!(), + } { + match toplevel { + Kind::Xdg(xdg) => xdg.with_pending_state(|state| { + if tiled { + state.states.set(XdgState::TiledLeft); + state.states.set(XdgState::TiledRight); + state.states.set(XdgState::TiledTop); + state.states.set(XdgState::TiledBottom); + } else { + state.states.unset(XdgState::TiledLeft); + state.states.unset(XdgState::TiledRight); + state.states.unset(XdgState::TiledTop); + state.states.unset(XdgState::TiledBottom); + } + }), + // Kind::X11? + }; + } + } + + pub fn is_tiled(&self) -> bool { + let window = match &self.element { + CosmicMappedInternal::Stack(s) => s.active(), + CosmicMappedInternal::Window(w) => w.window.clone(), + _ => unreachable!(), + }; + + match window.toplevel() { + Kind::Xdg(xdg) => xdg.current_state().states.contains(XdgState::TiledLeft), + // Kind::X11? + } + } + + pub fn set_fullscreen(&self, fullscreen: bool) { + for window in match &self.element { + CosmicMappedInternal::Stack(s) => { + Box::new(s.windows()) as Box> + } + CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())), + _ => unreachable!(), + } { + match window.toplevel() { + Kind::Xdg(xdg) => xdg.with_pending_state(|state| { + if fullscreen { + state.states.set(XdgState::Fullscreen); + } else { + state.states.unset(XdgState::Fullscreen); + } + }), + // Kind::X11? + }; + } + } + + pub fn is_fullscreen(&self) -> bool { + let window = match &self.element { + CosmicMappedInternal::Stack(s) => s.active(), + CosmicMappedInternal::Window(w) => w.window.clone(), + _ => unreachable!(), + }; + + match window.toplevel() { + Kind::Xdg(xdg) => xdg.current_state().states.contains(XdgState::Fullscreen), + // Kind::X11? + } + } + + pub fn set_maximized(&self, maximized: bool) { + for window in match &self.element { + CosmicMappedInternal::Stack(s) => { + Box::new(s.windows()) as Box> + } + CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())), + _ => unreachable!(), + } { + match window.toplevel() { + Kind::Xdg(xdg) => xdg.with_pending_state(|state| { + if maximized { + state.states.set(XdgState::Maximized); + } else { + state.states.unset(XdgState::Maximized); + } + }), + // Kind::X11? + }; + } + } + + pub fn is_maximized(&self) -> bool { + let window = match &self.element { + CosmicMappedInternal::Stack(s) => s.active(), + CosmicMappedInternal::Window(w) => w.window.clone(), + _ => unreachable!(), + }; + + match window.toplevel() { + Kind::Xdg(xdg) => xdg.current_state().states.contains(XdgState::Maximized), + // Kind::X11? + } + } + + pub fn set_activated(&self, activated: bool) { + for window in match &self.element { + CosmicMappedInternal::Stack(s) => { + Box::new(s.windows()) as Box> + } + CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())), + _ => unreachable!(), + } { + match window.toplevel() { + Kind::Xdg(xdg) => xdg.with_pending_state(|state| { + if activated { + state.states.set(XdgState::Activated); + } else { + state.states.unset(XdgState::Activated); + } + }), + // Kind::X11? + }; + } + } + + pub fn is_activated(&self) -> bool { + let window = match &self.element { + CosmicMappedInternal::Stack(s) => s.active(), + CosmicMappedInternal::Window(w) => w.window.clone(), + _ => unreachable!(), + }; + + match window.toplevel() { + Kind::Xdg(xdg) => xdg.current_state().states.contains(XdgState::Activated), + // Kind::X11? + } + } + + pub fn set_size(&self, size: Size) { + match &self.element { + CosmicMappedInternal::Stack(s) => s.set_size(size), + CosmicMappedInternal::Window(w) => w.set_size(size), + _ => {} + } + } + + pub fn min_size(&self) -> Size { + match &self.element { + CosmicMappedInternal::Stack(stack) => stack + .windows() + .fold(None, |min_size, window| { + let win_min_size = with_states(window.toplevel().wl_surface(), |states| { + let attrs = states + .data_map + .get::>() + .unwrap() + .lock() + .unwrap(); + attrs.min_size + }); + match (min_size, win_min_size) { + (None, x) => Some(x), + (Some(min1), min2) => Some((min1.w.max(min2.w), min1.h.max(min2.h)).into()), + } + }) + .expect("Empty stack?"), + CosmicMappedInternal::Window(window) => { + with_states(window.window.toplevel().wl_surface(), |states| { + let attrs = states + .data_map + .get::>() + .unwrap() + .lock() + .unwrap(); + attrs.min_size + }) + } + _ => unreachable!(), + } + } + + pub fn max_size(&self) -> Size { + match &self.element { + CosmicMappedInternal::Stack(stack) => { + let theoretical_max = stack.windows().fold(None, |max_size, window| { + let win_max_size = with_states(window.toplevel().wl_surface(), |states| { + let attrs = states + .data_map + .get::>() + .unwrap() + .lock() + .unwrap(); + attrs.max_size + }); + match (max_size, win_max_size) { + (None, x) => Some(x), + (Some(max1), max2) => Some( + ( + if max1.w == 0 { + max2.w + } else if max2.w == 0 { + max1.w + } else { + max1.w.min(max2.w) + }, + if max1.h == 0 { + max2.h + } else if max2.h == 0 { + max1.h + } else { + max1.h.min(max2.h) + }, + ) + .into(), + ), + } + }); + // The problem is, with accumulated sizes, the minimum size could be larger than our maximum... + let min_size = self.min_size(); + match (theoretical_max, min_size) { + (None, _) => (0, 0).into(), + (Some(max), min) => (max.w.max(min.w), max.h.max(min.h)).into(), + } + } + CosmicMappedInternal::Window(window) => { + with_states(window.window.toplevel().wl_surface(), |states| { + let attrs = states + .data_map + .get::>() + .unwrap() + .lock() + .unwrap(); + attrs.max_size + }) + } + _ => unreachable!(), + } + } + + pub fn configure(&self) { + for window in match &self.element { + CosmicMappedInternal::Stack(s) => { + Box::new(s.windows()) as Box> + } + CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())), + _ => unreachable!(), + } { + match window.toplevel() { + Kind::Xdg(xdg) => xdg.send_configure(), + // Kind::X11? + }; + } + } + + pub fn send_close(&self) { + let window = match &self.element { + CosmicMappedInternal::Stack(s) => s.active(), + CosmicMappedInternal::Window(w) => w.window.clone(), + _ => unreachable!(), + }; + + match window.toplevel() { + Kind::Xdg(xdg) => xdg.send_close(), + // Kind::X11? + }; + } +} + +impl IsAlive for CosmicMapped { + fn alive(&self) -> bool { + self.element.alive() + } +} + +impl SpaceElement for CosmicMapped { + fn bbox(&self) -> Rectangle { + SpaceElement::bbox(&self.element) + } + fn is_in_input_region(&self, point: &Point) -> bool { + SpaceElement::is_in_input_region(&self.element, point) + } + fn set_activate(&self, activated: bool) { + SpaceElement::set_activate(&self.element, activated) + } + fn output_enter(&self, output: &Output, overlap: Rectangle) { + SpaceElement::output_enter(&self.element, output, overlap) + } + fn output_leave(&self, output: &Output) { + SpaceElement::output_leave(&self.element, output) + } + fn geometry(&self) -> Rectangle { + SpaceElement::geometry(&self.element) + } + fn z_index(&self) -> u8 { + SpaceElement::z_index(&self.element) + } + fn refresh(&self) { + SpaceElement::refresh(&self.element) + } +} + +impl KeyboardTarget for CosmicMapped { + fn enter( + &self, + seat: &Seat, + data: &mut State, + keys: Vec>, + serial: Serial, + ) { + match &self.element { + CosmicMappedInternal::Stack(s) => KeyboardTarget::enter(s, seat, data, keys, serial), + CosmicMappedInternal::Window(w) => KeyboardTarget::enter(w, seat, data, keys, serial), + _ => {} + } + } + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial) { + match &self.element { + CosmicMappedInternal::Stack(s) => KeyboardTarget::leave(s, seat, data, serial), + CosmicMappedInternal::Window(w) => KeyboardTarget::leave(w, seat, data, serial), + _ => {} + } + } + fn key( + &self, + seat: &Seat, + data: &mut State, + key: KeysymHandle<'_>, + state: KeyState, + serial: Serial, + time: u32, + ) { + match &self.element { + CosmicMappedInternal::Stack(s) => { + KeyboardTarget::key(s, seat, data, key, state, serial, time) + } + CosmicMappedInternal::Window(w) => { + KeyboardTarget::key(w, seat, data, key, state, serial, time) + } + _ => {} + } + } + fn modifiers( + &self, + seat: &Seat, + data: &mut State, + modifiers: ModifiersState, + serial: Serial, + ) { + match &self.element { + CosmicMappedInternal::Stack(s) => { + KeyboardTarget::modifiers(s, seat, data, modifiers, serial) + } + CosmicMappedInternal::Window(w) => { + KeyboardTarget::modifiers(w, seat, data, modifiers, serial) + } + _ => {} + } + } +} + +impl PointerTarget for CosmicMapped { + fn enter(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { + match &self.element { + CosmicMappedInternal::Stack(s) => PointerTarget::enter(s, seat, data, event), + CosmicMappedInternal::Window(w) => PointerTarget::enter(w, seat, data, event), + _ => {} + } + } + fn motion(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { + match &self.element { + CosmicMappedInternal::Stack(s) => PointerTarget::motion(s, seat, data, event), + CosmicMappedInternal::Window(w) => PointerTarget::motion(w, seat, data, event), + _ => {} + } + } + fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) { + match &self.element { + CosmicMappedInternal::Stack(s) => PointerTarget::button(s, seat, data, event), + CosmicMappedInternal::Window(w) => PointerTarget::button(w, seat, data, event), + _ => {} + } + } + fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) { + match &self.element { + CosmicMappedInternal::Stack(s) => PointerTarget::axis(s, seat, data, frame), + CosmicMappedInternal::Window(w) => PointerTarget::axis(w, seat, data, frame), + _ => {} + } + } + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) { + match &self.element { + CosmicMappedInternal::Stack(s) => PointerTarget::leave(s, seat, data, serial, time), + CosmicMappedInternal::Window(w) => PointerTarget::leave(w, seat, data, serial, time), + _ => {} + } + } +} + +impl WaylandFocus for CosmicMapped { + fn wl_surface(&self) -> Option { + match &self.element { + CosmicMappedInternal::Window(w) => w.window.wl_surface().clone(), + CosmicMappedInternal::Stack(s) => s.active().wl_surface().clone(), + _ => None, + } + } + + fn same_client_as(&self, object_id: &ObjectId) -> bool { + match &self.element { + CosmicMappedInternal::Window(w) => w.window.same_client_as(object_id), + CosmicMappedInternal::Stack(s) => s.windows().any(|w| w.same_client_as(object_id)), + _ => false, + } + } +} + +impl From for CosmicMapped { + fn from(w: CosmicWindow) -> Self { + CosmicMapped { + element: CosmicMappedInternal::Window(w), + tiling_node_id: Arc::new(Mutex::new(None)), + last_geometry: Arc::new(Mutex::new(None)), + } + } +} + +impl From for CosmicMapped { + fn from(s: CosmicStack) -> Self { + CosmicMapped { + element: CosmicMappedInternal::Stack(s), + tiling_node_id: Arc::new(Mutex::new(None)), + last_geometry: Arc::new(Mutex::new(None)), + } + } +} + +render_elements! { + pub CosmicMappedRenderElement where R: ImportAll; + Stack=self::stack::CosmicStackRenderElement, + Window=self::window::CosmicWindowRenderElement, +} + +impl AsRenderElements for CosmicMapped +where + R: Renderer + ImportAll, + ::TextureId: 'static, +{ + type RenderElement = CosmicMappedRenderElement; + fn render_elements>( + &self, + location: Point, + scale: Scale, + ) -> Vec { + match &self.element { + CosmicMappedInternal::Stack(s) => AsRenderElements::::render_elements::< + CosmicMappedRenderElement, + >(s, location, scale), + CosmicMappedInternal::Window(w) => AsRenderElements::::render_elements::< + CosmicMappedRenderElement, + >(w, location, scale), + _ => Vec::new(), + } + .into_iter() + .map(C::from) + .collect() + } +} diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs new file mode 100644 index 00000000..76fbadd4 --- /dev/null +++ b/src/shell/element/stack.rs @@ -0,0 +1,293 @@ +use crate::state::State; +use smithay::{ + backend::{ + input::KeyState, + renderer::{ + element::{surface::WaylandSurfaceRenderElement, AsRenderElements}, + ImportAll, Renderer, + }, + }, + desktop::{space::SpaceElement, Kind, Window}, + input::{ + keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, + pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget}, + Seat, + }, + output::Output, + render_elements, + utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size}, +}; +use std::{ + hash::Hash, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, Mutex, + }, +}; + +#[derive(Debug, Clone)] +pub struct CosmicStack { + windows: Arc>>, + active: Arc, + pub(super) header: Arc>>, +} + +impl PartialEq for CosmicStack { + fn eq(&self, other: &Self) -> bool { + *self.windows.lock().unwrap() == *other.windows.lock().unwrap() + } +} + +impl Eq for CosmicStack {} + +impl Hash for CosmicStack { + fn hash(&self, state: &mut H) { + Arc::as_ptr(&self.windows).hash(state) + } +} + +#[derive(Debug)] +pub struct HeaderBar {} + +impl HeaderBar { + pub fn height(&self) -> i32 { + 0 + } +} + +impl CosmicStack { + pub fn active(&self) -> Window { + self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)].clone() + } + + pub fn set_active(&self, window: &Window) { + if let Some(val) = self + .windows + .lock() + .unwrap() + .iter() + .position(|w| w == window) + { + self.active.store(val, Ordering::SeqCst) + } + } + + pub fn set_size(&self, size: Size) { + let surface_size = ( + size.w, + size.h + - self + .header + .lock() + .unwrap() + .as_ref() + .map(|h| h.height()) + .unwrap_or(0), + ) + .into(); + + for window in self.windows.lock().unwrap().iter() { + match window.toplevel() { + Kind::Xdg(xdg) => xdg.with_pending_state(|state| state.size = Some(surface_size)), + }; + } + } +} + +impl IsAlive for CosmicStack { + fn alive(&self) -> bool { + self.windows.lock().unwrap().iter().any(IsAlive::alive) + } +} + +impl SpaceElement for CosmicStack { + fn bbox(&self) -> Rectangle { + SpaceElement::bbox(&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)]) + } + fn is_in_input_region(&self, point: &Point) -> bool { + SpaceElement::is_in_input_region( + &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], + point, + ) + } + fn set_activate(&self, activated: bool) { + self.windows + .lock() + .unwrap() + .iter() + .for_each(|w| SpaceElement::set_activate(w, activated)) + } + fn output_enter(&self, output: &Output, overlap: Rectangle) { + self.windows + .lock() + .unwrap() + .iter() + .for_each(|w| SpaceElement::output_enter(w, output, overlap)) + } + fn output_leave(&self, output: &Output) { + self.windows + .lock() + .unwrap() + .iter() + .for_each(|w| SpaceElement::output_leave(w, output)) + } + fn geometry(&self) -> Rectangle { + SpaceElement::geometry(&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)]) + } + fn z_index(&self) -> u8 { + SpaceElement::z_index(&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)]) + } + fn refresh(&self) { + let mut windows = self.windows.lock().unwrap(); + windows.retain(IsAlive::alive); + let len = windows.len(); + let _ = self + .active + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |active| { + (active > len).then_some(len - 1) + }); + windows.iter().for_each(|w| SpaceElement::refresh(w)) + } +} + +impl KeyboardTarget for CosmicStack { + fn enter( + &self, + seat: &Seat, + data: &mut State, + keys: Vec>, + serial: Serial, + ) { + KeyboardTarget::enter( + &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], + seat, + data, + keys, + serial, + ) + } + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial) { + KeyboardTarget::leave( + &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], + seat, + data, + serial, + ) + } + fn key( + &self, + seat: &Seat, + data: &mut State, + key: KeysymHandle<'_>, + state: KeyState, + serial: Serial, + time: u32, + ) { + KeyboardTarget::key( + &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], + seat, + data, + key, + state, + serial, + time, + ) + } + fn modifiers( + &self, + seat: &Seat, + data: &mut State, + modifiers: ModifiersState, + serial: Serial, + ) { + KeyboardTarget::modifiers( + &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], + seat, + data, + modifiers, + serial, + ) + } +} + +impl PointerTarget for CosmicStack { + fn enter(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { + PointerTarget::enter( + &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], + seat, + data, + event, + ) + } + fn motion(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { + PointerTarget::motion( + &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], + seat, + data, + event, + ) + } + fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) { + PointerTarget::button( + &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], + seat, + data, + event, + ) + } + fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) { + PointerTarget::axis( + &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], + seat, + data, + frame, + ) + } + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) { + PointerTarget::leave( + &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], + seat, + data, + serial, + time, + ) + } +} + +render_elements! { + pub CosmicStackRenderElement where R: ImportAll; + Window=WaylandSurfaceRenderElement, +} + +impl AsRenderElements for CosmicStack +where + R: Renderer + ImportAll, + ::TextureId: 'static, +{ + type RenderElement = CosmicStackRenderElement; + fn render_elements>( + &self, + location: Point, + scale: Scale, + ) -> Vec { + AsRenderElements::::render_elements::>( + &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], + location, + scale, + ) + .into_iter() + .map(C::from) + .collect() + } +} +impl CosmicStack { + pub fn windows(&self) -> impl Iterator { + self.windows + .lock() + .unwrap() + .iter() + .cloned() + .collect::>() + .into_iter() + } +} diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs new file mode 100644 index 00000000..821b4ae4 --- /dev/null +++ b/src/shell/element/window.rs @@ -0,0 +1,231 @@ +use crate::state::State; +use smithay::{ + backend::{ + input::KeyState, + renderer::{ + element::{surface::WaylandSurfaceRenderElement, AsRenderElements}, + ImportAll, Renderer, + }, + }, + desktop::{space::SpaceElement, Kind, Window}, + input::{ + keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, + pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget}, + Seat, + }, + output::Output, + reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode as DecorationMode, + render_elements, + utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size}, + wayland::shell::xdg::ToplevelSurface, +}; +use std::{ + hash::Hash, + sync::{Arc, Mutex}, +}; + +#[derive(Debug, Clone)] +pub struct CosmicWindow { + pub(super) window: Window, + pub(super) header: Arc>>, +} + +impl PartialEq for CosmicWindow { + fn eq(&self, other: &Window) -> bool { + &self.window == other + } +} + +impl PartialEq for Window { + fn eq(&self, other: &CosmicWindow) -> bool { + self == &other.window + } +} + +impl PartialEq for CosmicWindow { + fn eq(&self, other: &Self) -> bool { + self.window == other.window + } +} + +impl Eq for CosmicWindow {} + +impl Hash for CosmicWindow { + fn hash(&self, state: &mut H) { + self.window.hash(state) + } +} + +impl CosmicWindow { + pub fn set_size(&self, size: Size) { + let surface_size = ( + size.w, + size.h + - self + .header + .lock() + .unwrap() + .as_ref() + .map(|h| h.height()) + .unwrap_or(0), + ) + .into(); + match self.window.toplevel() { + Kind::Xdg(xdg) => xdg.with_pending_state(|state| state.size = Some(surface_size)), + }; + } +} + +impl From for CosmicWindow { + fn from(window: Window) -> Self { + let is_ssd = matches!( + match window.toplevel() { + Kind::Xdg(xdg) => xdg.current_state().decoration_mode, + }, + Some(DecorationMode::ServerSide) + ); + CosmicWindow { + window, + header: Arc::new(Mutex::new(is_ssd.then_some(HeaderBar::default()))), + } + } +} + +impl From for CosmicWindow { + fn from(surf: ToplevelSurface) -> Self { + let is_ssd = matches!( + surf.current_state().decoration_mode, + Some(DecorationMode::ServerSide) + ); + CosmicWindow { + window: Window::new(Kind::Xdg(surf)), + header: Arc::new(Mutex::new(is_ssd.then_some(HeaderBar::default()))), + } + } +} + +#[derive(Debug, Default, PartialEq)] +pub(super) struct HeaderBar { + pointer_loc: Option>, + close_button_hover: bool, + maximize_button_hover: bool, +} + +impl HeaderBar { + pub fn height(&self) -> i32 { + 0 + } +} + +impl IsAlive for CosmicWindow { + fn alive(&self) -> bool { + self.window.alive() + } +} + +impl SpaceElement for CosmicWindow { + fn bbox(&self) -> Rectangle { + SpaceElement::bbox(&self.window) + } + fn is_in_input_region(&self, point: &Point) -> bool { + SpaceElement::is_in_input_region(&self.window, point) + } + fn set_activate(&self, activated: bool) { + SpaceElement::set_activate(&self.window, activated) + } + fn output_enter(&self, output: &Output, overlap: Rectangle) { + SpaceElement::output_enter(&self.window, output, overlap) + } + fn output_leave(&self, output: &Output) { + SpaceElement::output_leave(&self.window, output) + } + fn geometry(&self) -> Rectangle { + SpaceElement::geometry(&self.window) + } + fn z_index(&self) -> u8 { + SpaceElement::z_index(&self.window) + } + fn refresh(&self) { + SpaceElement::refresh(&self.window) + } +} + +impl KeyboardTarget for CosmicWindow { + fn enter( + &self, + seat: &Seat, + data: &mut State, + keys: Vec>, + serial: Serial, + ) { + KeyboardTarget::enter(&self.window, seat, data, keys, serial) + } + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial) { + KeyboardTarget::leave(&self.window, seat, data, serial) + } + fn key( + &self, + seat: &Seat, + data: &mut State, + key: KeysymHandle<'_>, + state: KeyState, + serial: Serial, + time: u32, + ) { + KeyboardTarget::key(&self.window, seat, data, key, state, serial, time) + } + fn modifiers( + &self, + seat: &Seat, + data: &mut State, + modifiers: ModifiersState, + serial: Serial, + ) { + KeyboardTarget::modifiers(&self.window, seat, data, modifiers, serial) + } +} + +impl PointerTarget for CosmicWindow { + fn enter(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { + PointerTarget::enter(&self.window, seat, data, event) + } + fn motion(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { + PointerTarget::motion(&self.window, seat, data, event) + } + fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) { + PointerTarget::button(&self.window, seat, data, event) + } + fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) { + PointerTarget::axis(&self.window, seat, data, frame) + } + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) { + PointerTarget::leave(&self.window, seat, data, serial, time) + } +} + +render_elements! { + pub CosmicWindowRenderElement where R: ImportAll; + Window=WaylandSurfaceRenderElement, +} + +impl AsRenderElements for CosmicWindow +where + R: Renderer + ImportAll, + ::TextureId: 'static, +{ + type RenderElement = CosmicWindowRenderElement; + fn render_elements>( + &self, + location: Point, + scale: Scale, + ) -> Vec { + AsRenderElements::::render_elements::>( + &self.window, + location - self.window.geometry().loc.to_physical_precise_round(scale), + scale, + ) + .into_iter() + .map(C::from) + .collect() + } +} diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index 09886893..6adb1245 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -1,24 +1,18 @@ use crate::{ - shell::{OutputBoundState, Shell, Workspace, WorkspaceMode}, + shell::{element::CosmicMapped, Shell, Workspace}, state::Common, utils::prelude::*, wayland::handlers::xdg_shell::PopupGrabData, }; use indexmap::IndexSet; use smithay::{ - desktop::{layer_map_for_output, PopupUngrabStrategy, Window, WindowSurfaceType}, + desktop::{layer_map_for_output, PopupUngrabStrategy}, input::Seat, - reexports::wayland_server::protocol::wl_surface::WlSurface, utils::{IsAlive, Serial, SERIAL_COUNTER}, - wayland::{ - compositor::get_role, - shell::{wlr_layer::LAYER_SURFACE_ROLE, xdg::XDG_TOPLEVEL_ROLE}, - }, -}; -use std::{ - cell::{Ref, RefCell, RefMut}, - collections::HashMap, }; +use std::cell::RefCell; + +use self::target::{KeyboardFocusTarget, WindowGroup}; pub mod target; @@ -32,80 +26,59 @@ pub enum FocusDirection { Out, } -pub struct FocusStack<'a>(Ref<'a, IndexSet>); -pub struct FocusStackMut<'a>(RefMut<'a, IndexSet>); +pub struct FocusStack<'a>(pub(super) Option<&'a IndexSet>); +pub struct FocusStackMut<'a>(pub(super) &'a mut IndexSet); impl<'a> FocusStack<'a> { - pub fn last(&self) -> Option { - self.0.iter().rev().find(|w| w.toplevel().alive()).cloned() + pub fn last(&self) -> Option<&CosmicMapped> { + self.0 + .as_ref() + .and_then(|set| set.iter().rev().find(|w| w.alive())) } - pub fn iter(&self) -> impl Iterator { - self.0.iter().rev().filter(|w| w.toplevel().alive()) + pub fn iter(&self) -> impl Iterator { + self.0 + .iter() + .flat_map(|set| set.iter().rev().filter(|w| w.alive())) } } impl<'a> FocusStackMut<'a> { - pub fn append(&mut self, window: &Window) { - self.0.retain(|w| w.toplevel().alive()); + pub fn append(&mut self, window: &CosmicMapped) { + self.0.retain(|w| w.alive()); self.0.shift_remove(window); self.0.insert(window.clone()); } - pub fn last(&self) -> Option { - self.0.iter().rev().find(|w| w.toplevel().alive()).cloned() + pub fn last(&self) -> Option<&CosmicMapped> { + self.0.iter().rev().find(|w| w.alive()) } - pub fn iter(&self) -> impl Iterator { - self.0.iter().rev().filter(|w| w.toplevel().alive()) + pub fn iter(&self) -> impl Iterator { + self.0.iter().rev().filter(|w| w.alive()) } } -type FocusStackData = RefCell<(HashMap>, IndexSet)>; +impl Workspace {} -impl Workspace { - pub fn focus_stack<'a, 'b>(&'b self, seat: &'a Seat) -> FocusStack<'a> { - seat.user_data() - .insert_if_missing(|| FocusStackData::new((HashMap::new(), IndexSet::new()))); - let idx = self.idx; - FocusStack(Ref::map( - seat.user_data().get::().unwrap().borrow(), - |map| map.0.get(&idx).unwrap_or(&map.1), //TODO: workaround until Ref::filter_map goes stable - )) - } - - pub fn focus_stack_mut<'a, 'b>(&'b self, seat: &'a Seat) -> FocusStackMut<'a> { - seat.user_data() - .insert_if_missing(|| FocusStackData::new((HashMap::new(), IndexSet::new()))); - let idx = self.idx; - FocusStackMut(RefMut::map( - seat.user_data() - .get::() - .unwrap() - .borrow_mut(), - |map| map.0.entry(idx).or_insert_with(|| IndexSet::new()), - )) - } -} - -pub struct ActiveFocus(RefCell>); +pub struct ActiveFocus(RefCell>); impl ActiveFocus { - fn set(seat: &Seat, surface: Option) { + fn set(seat: &Seat, target: Option) { if !seat .user_data() - .insert_if_missing(|| ActiveFocus(RefCell::new(surface.clone()))) + .insert_if_missing(|| ActiveFocus(RefCell::new(target.clone()))) { *seat .user_data() .get::() .unwrap() .0 - .borrow_mut() = surface; + .borrow_mut() = target; } } - fn get(seat: &Seat) -> Option { + fn get(seat: &Seat) -> Option { seat.user_data() .get::() .and_then(|a| a.0.borrow().clone()) @@ -115,30 +88,25 @@ impl ActiveFocus { impl Shell { pub fn set_focus<'a>( state: &mut State, - surface: Option<&WlSurface>, + target: Option<&KeyboardFocusTarget>, active_seat: &Seat, serial: Option, ) { // update FocusStack and notify layouts about new focus (if any window) - if let Some(surface) = surface { - if let Some(workspace) = state.common.shell.space_for_window_mut(surface) { - if let Some(window) = workspace - .space - .window_for_surface(surface, WindowSurfaceType::ALL) - { - let mut focus_stack = workspace.focus_stack_mut(active_seat); - if Some(window) != focus_stack.last().as_ref() { - slog_scope::debug!("Focusing window: {:?}", window); - focus_stack.append(window); - // also remove popup grabs, if we are switching focus - if let Some(mut popup_grab) = active_seat - .user_data() - .get::() - .and_then(|x| x.take()) - { - if !popup_grab.has_ended() { - popup_grab.ungrab(PopupUngrabStrategy::All); - } + if let Some(KeyboardFocusTarget::Element(mapped)) = target { + if let Some(workspace) = state.common.shell.space_for_mut(mapped) { + let mut focus_stack = workspace.focus_stack.get_mut(active_seat); + if Some(mapped) != focus_stack.last() { + slog_scope::debug!("Focusing window: {:?}", mapped); + focus_stack.append(mapped); + // also remove popup grabs, if we are switching focus + if let Some(mut popup_grab) = active_seat + .user_data() + .get::() + .and_then(|x| x.take()) + { + if !popup_grab.has_ended() { + popup_grab.ungrab(PopupUngrabStrategy::All); } } } @@ -147,10 +115,10 @@ impl Shell { // update keyboard focus if let Some(keyboard) = active_seat.get_keyboard() { - ActiveFocus::set(active_seat, surface.cloned()); + ActiveFocus::set(active_seat, target.cloned()); keyboard.set_focus( state, - surface.cloned(), + target.cloned(), serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()), ); } @@ -160,30 +128,23 @@ impl Shell { // update activate status let focused_windows = seats .flat_map(|seat| { - self.outputs - .iter() - .flat_map(|o| self.active_space(o).focus_stack(seat).last().clone()) + self.outputs.iter().flat_map(|o| { + let space = self.active_space(o); + let stack = space.focus_stack.get(seat); + stack.last().cloned() + }) }) .collect::>(); for output in self.outputs.iter() { - let workspace = match &self.workspace_mode { - WorkspaceMode::OutputBound => { - let active = output - .user_data() - .get::() - .unwrap() - .active - .get(); - &mut self.spaces[active] - } - WorkspaceMode::Global { active, .. } => &mut self.spaces[*active], - }; + let workspace = self.workspaces.active_mut(output); for focused in focused_windows.iter() { - workspace.space.raise_window(focused, true); + if workspace.floating_layer.mapped().any(|m| m == focused) { + workspace.floating_layer.space.raise_element(focused, true); + } } - for window in workspace.space.windows() { - window.set_activated(focused_windows.contains(window)); + for window in workspace.mapped() { + window.set_activated(focused_windows.contains(&window)); window.configure(); } } @@ -193,55 +154,50 @@ impl Shell { impl Common { pub fn set_focus( state: &mut State, - surface: Option<&WlSurface>, + target: Option<&KeyboardFocusTarget>, active_seat: &Seat, serial: Option, ) { - Shell::set_focus(state, surface, active_seat, serial); - state.common.shell.update_active(state.common.seats.iter()); + Shell::set_focus(state, target, active_seat, serial); + let seats = state.common.seats().cloned().collect::>(); + state.common.shell.update_active(seats.iter()); } pub fn refresh_focus(state: &mut State) { - let seats = state.common.seats.clone(); + let seats = state.common.seats().cloned().collect::>(); for seat in seats { - let output = active_output(&seat, &state.common); + let output = seat.active_output(); let last_known_focus = ActiveFocus::get(&seat); - if let Some(surface) = last_known_focus { - if surface.alive() { - let is_toplevel = matches!(get_role(&surface), Some(XDG_TOPLEVEL_ROLE)); - let is_layer = matches!(get_role(&surface), Some(LAYER_SURFACE_ROLE)); - - if let Some(popup) = state.common.shell.popups.find_popup(&surface) { - if popup.alive() { - continue; - } - } else if is_layer { - if layer_map_for_output(&output) - .layer_for_surface(&surface, WindowSurfaceType::ALL) - .is_some() - { - continue; // Focus is valid - } - } else if is_toplevel { - let workspace = state.common.shell.active_space(&output); - if let Some(window) = workspace - .space - .window_for_surface(&surface, WindowSurfaceType::ALL) - { - let focus_stack = workspace.focus_stack(&seat); - if !focus_stack.last().map(|w| &w != window).unwrap_or(true) { + if let Some(target) = last_known_focus { + if target.alive() { + match target { + KeyboardFocusTarget::Element(mapped) => { + let workspace = state.common.shell.active_space(&output); + let focus_stack = workspace.focus_stack.get(&seat); + if focus_stack.last().map(|m| m == &mapped).unwrap_or(false) { continue; // Focus is valid } else { slog_scope::debug!("Wrong Window, focus fixup"); } - } else { - slog_scope::debug!("Different workspaces Window, focus fixup"); } - } else { - // unknown surface type, fixup - slog_scope::debug!("Surface unmapped, focus fixup"); - } + KeyboardFocusTarget::LayerSurface(layer) => { + if layer_map_for_output(&output).layers().any(|l| l == &layer) { + continue; // Focus is valid + } + } + KeyboardFocusTarget::Group(WindowGroup { + output: weak_output, + .. + }) => { + if weak_output == output { + continue; // Focus is valid, + } + } + KeyboardFocusTarget::Popup(_) | KeyboardFocusTarget::Fullscreen(_) => { + continue; + } // Focus is valid + }; } else { slog_scope::debug!("Surface dead, focus fixup"); } @@ -263,21 +219,24 @@ impl Common { } // update keyboard focus - let surface = state + let target = state .common .shell .active_space(&output) - .focus_stack(&seat) + .focus_stack + .get(&seat) .last() - .map(|w| w.toplevel().wl_surface().clone()); + .cloned() + .map(KeyboardFocusTarget::from); if let Some(keyboard) = seat.get_keyboard() { - slog_scope::info!("restoring focus to: {:?}", surface.as_ref()); - keyboard.set_focus(state, surface.clone(), SERIAL_COUNTER.next_serial()); - ActiveFocus::set(&seat, surface); + slog_scope::info!("restoring focus to: {:?}", target.as_ref()); + keyboard.set_focus(state, target.clone(), SERIAL_COUNTER.next_serial()); + ActiveFocus::set(&seat, target); } } } - state.common.shell.update_active(state.common.seats.iter()) + let seats = state.common.seats().cloned().collect::>(); + state.common.shell.update_active(seats.iter()) } } diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index 81861424..6781bf08 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -1,5 +1,8 @@ -use crate::utils::prelude::*; -pub use smithay::{ +use std::sync::Weak; + +use crate::{shell::element::CosmicMapped, utils::prelude::*}; +use id_tree::NodeId; +use smithay::{ backend::input::KeyState, desktop::{LayerSurface, PopupKind, Window}, input::{ @@ -7,83 +10,139 @@ pub use smithay::{ pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget}, Seat, }, + output::WeakOutput, reexports::wayland_server::{backend::ObjectId, protocol::wl_surface::WlSurface, Resource}, utils::{IsAlive, Serial}, wayland::seat::WaylandFocus, }; #[derive(Debug, Clone, PartialEq)] -pub enum FocusTarget { - Window(Window), +pub enum PointerFocusTarget { + Element(CosmicMapped), + Fullscreen(Window), LayerSurface(LayerSurface), Popup(PopupKind), } -impl IsAlive for FocusTarget { - fn alive(&self) -> bool { - match self { - FocusTarget::Window(w) => w.alive(), - FocusTarget::LayerSurface(l) => l.alive(), - FocusTarget::Popup(p) => p.alive(), +#[derive(Debug, Clone, PartialEq)] +pub enum KeyboardFocusTarget { + Element(CosmicMapped), + Fullscreen(Window), + Group(WindowGroup), + LayerSurface(LayerSurface), + Popup(PopupKind), +} + +impl From for PointerFocusTarget { + fn from(target: KeyboardFocusTarget) -> Self { + match target { + KeyboardFocusTarget::Element(elem) => PointerFocusTarget::Element(elem), + KeyboardFocusTarget::Fullscreen(elem) => PointerFocusTarget::Fullscreen(elem), + KeyboardFocusTarget::LayerSurface(layer) => PointerFocusTarget::LayerSurface(layer), + KeyboardFocusTarget::Popup(popup) => PointerFocusTarget::Popup(popup), + _ => unreachable!("A window grab cannot start a popup grab"), } } } -impl PointerTarget for FocusTarget { +#[derive(Debug, Clone)] +pub struct WindowGroup { + pub(in crate::shell) node: NodeId, + pub(in crate::shell) output: WeakOutput, + pub(in crate::shell) alive: Weak<()>, +} + +impl PartialEq for WindowGroup { + fn eq(&self, other: &Self) -> bool { + self.node == other.node + && self.output == other.output + && Weak::ptr_eq(&self.alive, &other.alive) + } +} + +impl IsAlive for PointerFocusTarget { + fn alive(&self) -> bool { + match self { + PointerFocusTarget::Element(e) => e.alive(), + PointerFocusTarget::Fullscreen(f) => f.alive(), + PointerFocusTarget::LayerSurface(l) => l.alive(), + PointerFocusTarget::Popup(p) => p.alive(), + } + } +} + +impl IsAlive for KeyboardFocusTarget { + fn alive(&self) -> bool { + match self { + KeyboardFocusTarget::Element(e) => e.alive(), + KeyboardFocusTarget::Fullscreen(f) => f.alive(), + KeyboardFocusTarget::Group(g) => g.alive.upgrade().is_some(), + KeyboardFocusTarget::LayerSurface(l) => l.alive(), + KeyboardFocusTarget::Popup(p) => p.alive(), + } + } +} + +impl PointerTarget for PointerFocusTarget { fn enter(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { match self { - FocusTarget::Window(w) => { - PointerTarget::enter(w.toplevel().wl_surface(), seat, data, event) + PointerFocusTarget::Element(w) => PointerTarget::enter(w, seat, data, event), + PointerFocusTarget::Fullscreen(w) => PointerTarget::enter(w, seat, data, event), + PointerFocusTarget::LayerSurface(l) => { + PointerTarget::enter(l.wl_surface(), seat, data, event) } - FocusTarget::LayerSurface(l) => PointerTarget::enter(l.wl_surface(), seat, data, event), - FocusTarget::Popup(p) => PointerTarget::enter(p.wl_surface(), seat, data, event), + PointerFocusTarget::Popup(p) => PointerTarget::enter(p.wl_surface(), seat, data, event), } } fn motion(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { match self { - FocusTarget::Window(w) => { - PointerTarget::motion(w.toplevel().wl_surface(), seat, data, event) - } - FocusTarget::LayerSurface(l) => { + PointerFocusTarget::Element(w) => PointerTarget::motion(w, seat, data, event), + PointerFocusTarget::Fullscreen(w) => PointerTarget::motion(w, seat, data, event), + PointerFocusTarget::LayerSurface(l) => { PointerTarget::motion(l.wl_surface(), seat, data, event) } - FocusTarget::Popup(p) => PointerTarget::motion(p.wl_surface(), seat, data, event), + PointerFocusTarget::Popup(p) => { + PointerTarget::motion(p.wl_surface(), seat, data, event) + } } } fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) { match self { - FocusTarget::Window(w) => { - PointerTarget::button(w.toplevel().wl_surface(), seat, data, event) - } - FocusTarget::LayerSurface(l) => { + PointerFocusTarget::Element(w) => PointerTarget::button(w, seat, data, event), + PointerFocusTarget::Fullscreen(w) => PointerTarget::button(w, seat, data, event), + PointerFocusTarget::LayerSurface(l) => { PointerTarget::button(l.wl_surface(), seat, data, event) } - FocusTarget::Popup(p) => PointerTarget::button(p.wl_surface(), seat, data, event), + PointerFocusTarget::Popup(p) => { + PointerTarget::button(p.wl_surface(), seat, data, event) + } } } fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) { match self { - FocusTarget::Window(w) => { - PointerTarget::axis(w.toplevel().wl_surface(), seat, data, frame) + PointerFocusTarget::Element(w) => PointerTarget::axis(w, seat, data, frame), + PointerFocusTarget::Fullscreen(w) => PointerTarget::axis(w, seat, data, frame), + PointerFocusTarget::LayerSurface(l) => { + PointerTarget::axis(l.wl_surface(), seat, data, frame) } - FocusTarget::LayerSurface(l) => PointerTarget::axis(l.wl_surface(), seat, data, frame), - FocusTarget::Popup(p) => PointerTarget::axis(p.wl_surface(), seat, data, frame), + PointerFocusTarget::Popup(p) => PointerTarget::axis(p.wl_surface(), seat, data, frame), } } fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) { match self { - FocusTarget::Window(w) => { - PointerTarget::leave(w.toplevel().wl_surface(), seat, data, serial, time) - } - FocusTarget::LayerSurface(l) => { + PointerFocusTarget::Element(w) => PointerTarget::leave(w, seat, data, serial, time), + PointerFocusTarget::Fullscreen(w) => PointerTarget::leave(w, seat, data, serial, time), + PointerFocusTarget::LayerSurface(l) => { PointerTarget::leave(l.wl_surface(), seat, data, serial, time) } - FocusTarget::Popup(p) => PointerTarget::leave(p.wl_surface(), seat, data, serial, time), + PointerFocusTarget::Popup(p) => { + PointerTarget::leave(p.wl_surface(), seat, data, serial, time) + } } } } -impl KeyboardTarget for FocusTarget { +impl KeyboardTarget for KeyboardFocusTarget { fn enter( &self, seat: &Seat, @@ -92,26 +151,30 @@ impl KeyboardTarget for FocusTarget { serial: Serial, ) { match self { - FocusTarget::Window(w) => { - KeyboardTarget::enter(w.toplevel().wl_surface(), seat, data, keys, serial) + KeyboardFocusTarget::Element(w) => KeyboardTarget::enter(w, seat, data, keys, serial), + KeyboardFocusTarget::Fullscreen(w) => { + KeyboardTarget::enter(w, seat, data, keys, serial) } - FocusTarget::LayerSurface(l) => { + KeyboardFocusTarget::Group(_) => {} + KeyboardFocusTarget::LayerSurface(l) => { KeyboardTarget::enter(l.wl_surface(), seat, data, keys, serial) } - FocusTarget::Popup(p) => { + KeyboardFocusTarget::Popup(p) => { KeyboardTarget::enter(p.wl_surface(), seat, data, keys, serial) } } } fn leave(&self, seat: &Seat, data: &mut State, serial: Serial) { match self { - FocusTarget::Window(w) => { - KeyboardTarget::leave(w.toplevel().wl_surface(), seat, data, serial) - } - FocusTarget::LayerSurface(l) => { + KeyboardFocusTarget::Element(w) => KeyboardTarget::leave(w, seat, data, serial), + KeyboardFocusTarget::Fullscreen(w) => KeyboardTarget::leave(w, seat, data, serial), + KeyboardFocusTarget::Group(_) => {} + KeyboardFocusTarget::LayerSurface(l) => { KeyboardTarget::leave(l.wl_surface(), seat, data, serial) } - FocusTarget::Popup(p) => KeyboardTarget::leave(p.wl_surface(), seat, data, serial), + KeyboardFocusTarget::Popup(p) => { + KeyboardTarget::leave(p.wl_surface(), seat, data, serial) + } } } fn key( @@ -124,19 +187,17 @@ impl KeyboardTarget for FocusTarget { time: u32, ) { match self { - FocusTarget::Window(w) => KeyboardTarget::key( - w.toplevel().wl_surface(), - seat, - data, - key, - state, - serial, - time, - ), - FocusTarget::LayerSurface(l) => { + KeyboardFocusTarget::Element(w) => { + KeyboardTarget::key(w, seat, data, key, state, serial, time) + } + KeyboardFocusTarget::Fullscreen(w) => { + KeyboardTarget::key(w, seat, data, key, state, serial, time) + } + KeyboardFocusTarget::Group(_) => {} + KeyboardFocusTarget::LayerSurface(l) => { KeyboardTarget::key(l.wl_surface(), seat, data, key, state, serial, time) } - FocusTarget::Popup(p) => { + KeyboardFocusTarget::Popup(p) => { KeyboardTarget::key(p.wl_surface(), seat, data, key, state, serial, time) } } @@ -149,50 +210,113 @@ impl KeyboardTarget for FocusTarget { serial: Serial, ) { match self { - FocusTarget::Window(w) => { - KeyboardTarget::modifiers(w.toplevel().wl_surface(), seat, data, modifiers, serial) + KeyboardFocusTarget::Element(w) => { + KeyboardTarget::modifiers(w, seat, data, modifiers, serial) } - FocusTarget::LayerSurface(l) => { + KeyboardFocusTarget::Fullscreen(w) => { + KeyboardTarget::modifiers(w, seat, data, modifiers, serial) + } + KeyboardFocusTarget::Group(_) => {} + KeyboardFocusTarget::LayerSurface(l) => { KeyboardTarget::modifiers(l.wl_surface(), seat, data, modifiers, serial) } - FocusTarget::Popup(p) => { + KeyboardFocusTarget::Popup(p) => { KeyboardTarget::modifiers(p.wl_surface(), seat, data, modifiers, serial) } } } } -impl WaylandFocus for FocusTarget { - fn wl_surface(&self) -> Option<&WlSurface> { - Some(match self { - FocusTarget::Window(w) => w.toplevel().wl_surface(), - FocusTarget::LayerSurface(l) => l.wl_surface(), - FocusTarget::Popup(p) => p.wl_surface(), - }) +impl WaylandFocus for KeyboardFocusTarget { + fn wl_surface(&self) -> Option { + match self { + KeyboardFocusTarget::Element(w) => WaylandFocus::wl_surface(w), + KeyboardFocusTarget::Fullscreen(w) => WaylandFocus::wl_surface(w), + KeyboardFocusTarget::Group(_) => None, + KeyboardFocusTarget::LayerSurface(l) => Some(l.wl_surface().clone()), + KeyboardFocusTarget::Popup(p) => Some(p.wl_surface().clone()), + } } fn same_client_as(&self, object_id: &ObjectId) -> bool { match self { - FocusTarget::Window(w) => w.toplevel().wl_surface().id().same_client_as(object_id), - FocusTarget::LayerSurface(l) => l.wl_surface().id().same_client_as(object_id), - FocusTarget::Popup(p) => p.wl_surface().id().same_client_as(object_id), + KeyboardFocusTarget::Element(w) => WaylandFocus::same_client_as(w, object_id), + KeyboardFocusTarget::Fullscreen(w) => WaylandFocus::same_client_as(w, object_id), + KeyboardFocusTarget::Group(_) => false, + KeyboardFocusTarget::LayerSurface(l) => l.wl_surface().id().same_client_as(object_id), + KeyboardFocusTarget::Popup(p) => p.wl_surface().id().same_client_as(object_id), } } } -impl From for FocusTarget { +impl WaylandFocus for PointerFocusTarget { + fn wl_surface(&self) -> Option { + Some(match self { + PointerFocusTarget::Element(w) => WaylandFocus::wl_surface(w)?, + PointerFocusTarget::Fullscreen(w) => WaylandFocus::wl_surface(w)?, + PointerFocusTarget::LayerSurface(l) => l.wl_surface().clone(), + PointerFocusTarget::Popup(p) => p.wl_surface().clone(), + }) + } + fn same_client_as(&self, object_id: &ObjectId) -> bool { + match self { + PointerFocusTarget::Element(w) => WaylandFocus::same_client_as(w, object_id), + PointerFocusTarget::Fullscreen(w) => WaylandFocus::same_client_as(w, object_id), + PointerFocusTarget::LayerSurface(l) => l.wl_surface().id().same_client_as(object_id), + PointerFocusTarget::Popup(p) => p.wl_surface().id().same_client_as(object_id), + } + } +} + +impl From for PointerFocusTarget { + fn from(w: CosmicMapped) -> Self { + PointerFocusTarget::Element(w) + } +} + +impl From for PointerFocusTarget { fn from(w: Window) -> Self { - FocusTarget::Window(w) + PointerFocusTarget::Fullscreen(w) } } -impl From for FocusTarget { +impl From for PointerFocusTarget { fn from(l: LayerSurface) -> Self { - FocusTarget::LayerSurface(l) + PointerFocusTarget::LayerSurface(l) } } -impl From for FocusTarget { +impl From for PointerFocusTarget { fn from(p: PopupKind) -> Self { - FocusTarget::Popup(p) + PointerFocusTarget::Popup(p) + } +} + +impl From for KeyboardFocusTarget { + fn from(w: CosmicMapped) -> Self { + KeyboardFocusTarget::Element(w) + } +} + +impl From for KeyboardFocusTarget { + fn from(w: Window) -> Self { + KeyboardFocusTarget::Fullscreen(w) + } +} + +impl From for KeyboardFocusTarget { + fn from(w: WindowGroup) -> Self { + KeyboardFocusTarget::Group(w) + } +} + +impl From for KeyboardFocusTarget { + fn from(l: LayerSurface) -> Self { + KeyboardFocusTarget::LayerSurface(l) + } +} + +impl From for KeyboardFocusTarget { + fn from(p: PopupKind) -> Self { + KeyboardFocusTarget::Popup(p) } } diff --git a/src/shell/grabs.rs b/src/shell/grabs.rs index cdb06a57..d27a2f62 100644 --- a/src/shell/grabs.rs +++ b/src/shell/grabs.rs @@ -51,10 +51,11 @@ impl Shell { .space .outputs_for_window(&window) .into_iter() - .find(|o| o.geometry().contains(pos.to_i32_round())) { - Some(o) => o, - None => return, - }; + .find(|o| o.geometry().contains(pos.to_i32_round())) + { + Some(o) => o, + None => return, + }; let mut initial_window_location = workspace.space.window_location(&window).unwrap(); match &window.toplevel() { @@ -356,7 +357,7 @@ impl MoveSurfaceGrab { time: u32, ) { // No more buttons are pressed, release the grab. - let output = active_output(&self.seat, &state.common); + let output = self.seat.active_output(); let seat = self.seat.clone(); state.common.event_loop_handle.insert_idle(move |data| { diff --git a/src/shell/layout/floating/grabs.rs b/src/shell/layout/floating/grabs.rs index f1ad8f1f..800150aa 100644 --- a/src/shell/layout/floating/grabs.rs +++ b/src/shell/layout/floating/grabs.rs @@ -1,16 +1,13 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::utils::prelude::*; +use crate::{shell::focus::target::PointerFocusTarget, utils::prelude::*}; use smithay::{ desktop::{Kind, Window}, input::pointer::{ AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle, }, - reexports::{ - wayland_protocols::xdg::shell::server::xdg_toplevel, - wayland_server::protocol::wl_surface::WlSurface, - }, + reexports::wayland_protocols::xdg::shell::server::xdg_toplevel, utils::{IsAlive, Logical, Point, Serial, Size}, wayland::{ compositor::with_states, @@ -90,7 +87,7 @@ impl PointerGrab for ResizeSurfaceGrab { &mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>, - _focus: Option<(WlSurface, Point)>, + _focus: Option<(PointerFocusTarget, Point)>, event: &MotionEvent, ) { // While the grab is active, no client has pointer focus diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 7cd99d0a..a64539c0 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -1,99 +1,86 @@ // SPDX-License-Identifier: GPL-3.0-only use smithay::{ - desktop::{layer_map_for_output, space::RenderZindex, Kind, Space, Window}, - input::{ - pointer::{Focus, GrabStartData as PointerGrabStartData}, - Seat, - }, + backend::renderer::{ImportAll, Renderer}, + desktop::{layer_map_for_output, space::SpaceElement, Space, Window}, + input::Seat, output::Output, - reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::{ - ResizeEdge, State as XdgState, - }, - utils::{IsAlive, Logical, Point, Rectangle, Serial}, - wayland::{compositor::with_states, shell::xdg::XdgToplevelSurfaceRoleAttributes}, + render_elements, + utils::{Logical, Point, Rectangle}, }; -use std::{collections::HashSet, sync::Mutex}; +use std::collections::HashMap; -use crate::state::State; +use crate::{ + shell::{ + element::{CosmicMapped, CosmicMappedRenderElement}, + OutputNotMapped, + }, + state::State, + utils::prelude::*, +}; mod grabs; pub use self::grabs::*; -pub const FLOATING_INDEX: u8 = RenderZindex::Shell as u8 + 1; - -#[derive(Debug, Default)] +#[derive(Debug)] pub struct FloatingLayout { - pending_windows: Vec, - pub windows: HashSet, + pub(in crate::shell) space: Space, } -#[derive(Default)] -pub struct WindowUserDataInner { - last_geometry: Rectangle, +impl Default for FloatingLayout { + fn default() -> Self { + FloatingLayout { + space: Space::new(None), + } + } } -pub type WindowUserData = Mutex; impl FloatingLayout { pub fn new() -> FloatingLayout { Default::default() } - pub fn map_window( + pub fn map_output(&mut self, output: &Output, location: Point) { + self.space.map_output(output, location) + } + + pub fn unmap_output(&mut self, output: &Output) { + self.space.unmap_output(output); + self.refresh(); + } + + pub fn map( &mut self, - space: &mut Space, - window: Window, + mapped: impl Into, seat: &Seat, position: impl Into>>, ) { - if let Some(output) = super::output_from_seat(Some(seat), space) { - self.map_window_internal(space, window, &output, position.into()); - } else { - self.pending_windows.push(window); - } + let mapped = mapped.into(); + let output = seat.active_output(); + let position = position.into(); + + self.map_internal(mapped, &output, position) } - pub fn refresh(&mut self, space: &mut Space) { - self.pending_windows.retain(|w| w.toplevel().alive()); - if let Some(output) = super::output_from_seat(None, space) { - for window in std::mem::take(&mut self.pending_windows).into_iter() { - self.map_window_internal(space, window, &output, None); - } - } - // TODO make sure all windows are still visible on any output or move them - } - - fn map_window_internal( + pub(in crate::shell) fn map_internal( &mut self, - space: &mut Space, - window: Window, + mapped: CosmicMapped, output: &Output, position: Option>, ) { - let last_geometry = window - .user_data() - .get::() - .map(|u| u.lock().unwrap().last_geometry); - let mut win_geo = window.geometry(); + let mut win_geo = mapped.geometry(); let layers = layer_map_for_output(&output); let geometry = layers.non_exclusive_zone(); + let last_geometry = mapped.last_geometry.lock().unwrap().clone(); let mut geo_updated = false; - if let Some(size) = last_geometry.clone().map(|g| g.size) { + if let Some(size) = last_geometry.map(|g| g.size) { geo_updated = win_geo.size == size; win_geo.size = size; } { - let (min_size, max_size) = with_states(window.toplevel().wl_surface(), |states| { - let attrs = states - .data_map - .get::>() - .unwrap() - .lock() - .unwrap(); - (attrs.min_size, attrs.max_size) - }); + let (min_size, max_size) = (mapped.min_size(), mapped.max_size()); if win_geo.size.w > geometry.size.w / 3 * 2 { // try a more reasonable size let mut width = geometry.size.w / 3 * 2; @@ -136,103 +123,67 @@ impl FloatingLayout { .into() }); - #[allow(irrefutable_let_patterns)] - if let Kind::Xdg(xdg) = &window.toplevel() { - xdg.with_pending_state(|state| { - state.states.unset(XdgState::TiledLeft); - state.states.unset(XdgState::TiledRight); - state.states.unset(XdgState::TiledTop); - state.states.unset(XdgState::TiledBottom); - if geo_updated { - state.size = Some(win_geo.size); - } - }); - xdg.send_configure(); + // TODO: Move this into CosmicMapped, this needs to differciate between stacks and windows + mapped.set_tiled(false); + if geo_updated { + mapped.set_size(win_geo.size); } + mapped.configure(); - space.map_window(&window, position, FLOATING_INDEX, false); - self.windows.insert(window); + self.space.map_element(mapped, position, false); } - pub fn unmap_window(&mut self, space: &mut Space, window: &Window) { + pub fn unmap(&mut self, window: &CosmicMapped) -> bool { #[allow(irrefutable_let_patterns)] - let is_maximized = match &window.toplevel() { - Kind::Xdg(surface) => { - surface.with_pending_state(|state| state.states.contains(XdgState::Maximized)) - } - }; + let is_maximized = window.is_maximized(); if !is_maximized { - if let Some(location) = space.window_location(window) { - let user_data = window.user_data(); - user_data.insert_if_missing(|| WindowUserData::default()); - user_data - .get::() - .unwrap() - .lock() - .unwrap() - .last_geometry = Rectangle::from_loc_and_size(location, window.geometry().size); + if let Some(location) = self.space.element_location(window) { + *window.last_geometry.lock().unwrap() = Some(Rectangle::from_loc_and_size( + location, + window.geometry().size, + )); } } - space.unmap_window(window); - self.pending_windows.retain(|w| w != window); - self.windows.remove(window); + let was_unmaped = self.space.elements().any(|e| e == window); + self.space.unmap_elem(&window); + was_unmaped } - pub fn maximize_request(&mut self, space: &mut Space, window: &Window, output: &Output) { + pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { + self.space.element_geometry(elem) + } + + pub fn maximize_request(&mut self, window: &CosmicMapped, output: &Output) { let layers = layer_map_for_output(&output); let geometry = layers.non_exclusive_zone(); - if let Some(location) = space.window_location(window) { - let user_data = window.user_data(); - user_data.insert_if_missing(|| WindowUserData::default()); - user_data - .get::() - .unwrap() - .lock() - .unwrap() - .last_geometry = Rectangle::from_loc_and_size(location, window.geometry().size); + if let Some(location) = self.space.element_location(window) { + *window.last_geometry.lock().unwrap() = Some(Rectangle::from_loc_and_size( + location, + window.geometry().size, + )); } - space.map_window( - &window, - (geometry.loc.x, geometry.loc.y), - FLOATING_INDEX, - true, - ); - #[allow(irrefutable_let_patterns)] - if let Kind::Xdg(surface) = &window.toplevel() { - surface.with_pending_state(|state| { - state.states.set(XdgState::Maximized); - state.size = Some(geometry.size); - }); - window.configure(); - } + self.space.map_element(window.clone(), geometry.loc, true); + window.set_maximized(true); + window.set_size(geometry.size); + window.configure(); } - pub fn unmaximize_request(&mut self, space: &mut Space, window: &Window) { - let last_geometry = window - .user_data() - .get::() - .map(|u| u.lock().unwrap().last_geometry); - match window.toplevel() { - Kind::Xdg(toplevel) => { - toplevel.with_pending_state(|state| { - state.states.unset(XdgState::Maximized); - state.size = last_geometry.map(|g| g.size); - }); - toplevel.send_configure(); - } - } - if let Some(last_location) = last_geometry.map(|g| g.loc) { - space.map_window(&window, last_location, FLOATING_INDEX, true); - } + pub fn unmaximize_request(&mut self, window: &CosmicMapped) { + let last_geometry = window.last_geometry.lock().unwrap().clone(); + window.set_maximized(false); + window.set_size(last_geometry.map(|g| g.size).expect("No previous size?")); + window.configure(); + let last_location = last_geometry.map(|g| g.loc).expect("No previous location?"); + self.space.map_element(window.clone(), last_location, true); } + /* pub fn resize_request( - state: &mut State, - window: &Window, + window: &CosmicWindow, seat: &Seat, serial: Serial, start_data: PointerGrabStartData, @@ -256,4 +207,111 @@ impl FloatingLayout { pointer.set_grab(state, grab, serial, Focus::Clear); } } + */ + + pub fn mapped(&self) -> impl Iterator { + self.space.elements() + } + + pub fn windows(&self) -> impl Iterator + '_ { + self.mapped().flat_map(|e| e.windows().map(|(w, _)| w)) + } + + pub fn refresh(&mut self) { + for element in self + .space + .elements() + .filter(|e| self.space.outputs_for_element(e).is_empty()) + .cloned() + .collect::>() + .into_iter() + { + // TODO what about windows leaving to the top with no headerbar to drag? can that happen? (Probably if the user is moving outputs down) + *element.last_geometry.lock().unwrap() = None; + let output = self.space.outputs().next().unwrap().clone(); + self.map_internal(element, &output, None); + } + self.space.refresh() + } + + pub fn most_overlapped_output_for_element(&self, elem: &CosmicMapped) -> Option { + let elem_geo = self.space.element_geometry(elem)?; + + if self.space.outputs().nth(1).is_none() { + return self.space.outputs().next().cloned(); + } + + Some( + self.space + .outputs_for_element(elem) + .into_iter() + .max_by_key(|o| { + let output_geo = self.space.output_geometry(o).unwrap(); + if let Some(intersection) = output_geo.intersection(elem_geo) { + intersection.size.w * intersection.size.h + } else { + 0 + } + }) + .unwrap_or(self.space.outputs().next().unwrap().clone()), + ) + } + + pub fn merge(&mut self, other: FloatingLayout) { + let mut output_pos_map = HashMap::new(); + for output in self.space.outputs() { + output_pos_map.insert( + output.clone(), + self.space.output_geometry(output).unwrap().loc + - other + .space + .output_geometry(output) + .map(|geo| geo.loc) + .unwrap_or_else(|| (0, 0).into()), + ); + } + for element in other.space.elements() { + let mut elem_geo = other.space.element_geometry(element).unwrap(); + let output = other + .space + .outputs_for_element(element) + .into_iter() + .filter(|o| self.space.outputs().any(|o2| o == o2)) + .max_by_key(|o| { + let output_geo = other.space.output_geometry(o).unwrap(); + let intersection = output_geo.intersection(elem_geo).unwrap(); + intersection.size.w * intersection.size.h + }) + .unwrap_or(self.space.outputs().next().unwrap().clone()); + elem_geo.loc += output_pos_map + .get(&output) + .copied() + .unwrap_or_else(|| (0, 0).into()); + self.space.map_element(element.clone(), elem_geo.loc, false); + } + self.refresh(); //fixup any out of bounds elements + } + + pub fn render_output( + &self, + output: &Output, + ) -> Result>, OutputNotMapped> + where + R: Renderer + ImportAll, + ::TextureId: 'static, + { + let output_scale = output.current_scale().fractional_scale(); + let output_geo = self.space.output_geometry(output).ok_or(OutputNotMapped)?; + Ok(self + .space + .render_elements_for_region::(&output_geo, output_scale) + .into_iter() + .map(FloatingRenderElement::from) + .collect()) + } +} + +render_elements! { + pub FloatingRenderElement where R: ImportAll; + Window=CosmicMappedRenderElement, } diff --git a/src/shell/layout/mod.rs b/src/shell/layout/mod.rs index 8f002037..24353c66 100644 --- a/src/shell/layout/mod.rs +++ b/src/shell/layout/mod.rs @@ -1,11 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::{input::ActiveOutput, state::State}; use regex::RegexSet; use smithay::{ - desktop::{Space, Window}, - input::Seat, - output::Output, + desktop::Window, wayland::{compositor::with_states, shell::xdg::XdgToplevelSurfaceRoleAttributes}, }; use std::sync::Mutex; @@ -118,17 +115,3 @@ pub fn should_be_floating(window: &Window) -> bool { false }) } - -fn output_from_seat(seat: Option<&Seat>, space: &Space) -> Option { - seat.and_then(|seat| { - seat.user_data() - .get::() - .map(|active| active.0.borrow().clone()) - .or_else(|| { - seat.get_pointer() - .map(|ptr| space.output_under(ptr.current_location()).next().unwrap()) - .cloned() - }) - }) - .or_else(|| space.outputs().next().cloned()) -} diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 5496e4e8..8a824935 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -1,63 +1,226 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - shell::{focus::FocusDirection, layout::Orientation}, + shell::{ + element::{CosmicMapped, CosmicMappedRenderElement}, + focus::{ + target::{KeyboardFocusTarget, WindowGroup}, + FocusDirection, + }, + layout::Orientation, + OutputNotMapped, + }, utils::prelude::*, }; -use atomic_float::AtomicF64; use id_tree::{InsertBehavior, MoveBehavior, Node, NodeId, NodeIdError, RemoveBehavior, Tree}; use smithay::{ - desktop::{layer_map_for_output, Kind, Space, Window}, + backend::renderer::{element::AsRenderElements, ImportAll, Renderer}, + desktop::{layer_map_for_output, Window}, input::{ pointer::{Focus, GrabStartData as PointerGrabStartData}, Seat, }, - reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::{ - ResizeEdge, State as XdgState, - }, - utils::{IsAlive, Rectangle, Serial}, + output::{Output, WeakOutput}, + render_elements, + utils::{IsAlive, Logical, Point, Rectangle, Scale, Serial}, }; use std::{ + borrow::Borrow, cell::RefCell, - collections::HashSet, - sync::{atomic::Ordering, Arc}, + collections::HashMap, + hash::Hash, + sync::{atomic::AtomicBool, Arc}, }; +/* mod grabs; pub use self::grabs::*; +*/ -#[derive(Debug)] -pub struct TilingLayout { - gaps: (i32, i32), - trees: Vec>, - pub windows: HashSet, +#[derive(Debug, Clone)] +struct OutputData { + output: Output, + location: Point, } -#[derive(Debug)] -pub enum Data { - Fork { - orientation: Orientation, - ratio: Arc, - }, - Stack { - active: usize, - len: usize, - }, - Window(Window), +impl Borrow for OutputData { + fn borrow(&self) -> &Output { + &self.output + } +} + +impl PartialEq for OutputData { + fn eq(&self, other: &Self) -> bool { + self.output == other.output + } +} + +impl Eq for OutputData {} + +impl PartialEq for OutputData { + fn eq(&self, other: &Output) -> bool { + &self.output == other + } +} + +impl Hash for OutputData { + fn hash(&self, state: &mut H) { + self.output.hash(state) + } } #[derive(Debug, Clone)] -pub struct WindowInfo { - node: NodeId, - output: usize, +pub struct TilingLayout { + gaps: (i32, i32), + trees: HashMap>, +} + +#[derive(Debug, Clone)] +pub enum Data { + Group { + orientation: Orientation, + sizes: Vec, + last_geometry: Rectangle, + alive: Arc<()>, + }, + Mapped { + mapped: CosmicMapped, + last_geometry: Rectangle, + }, } impl Data { - fn fork() -> Data { - Data::Fork { - orientation: Orientation::Vertical, - ratio: Arc::new(AtomicF64::new(0.5)), + fn new_group(orientation: Orientation, geo: Rectangle) -> Data { + Data::Group { + orientation, + sizes: vec![ + match orientation { + Orientation::Vertical => geo.size.w / 2, + Orientation::Horizontal => geo.size.h / 2, + }; + 2 + ], + last_geometry: geo, + alive: Arc::new(()), + } + } + + fn is_group(&self) -> bool { + matches!(self, Data::Group { .. }) + } + fn is_mapped(&self, mapped: Option<&CosmicMapped>) -> bool { + match mapped { + Some(m) => matches!(self, Data::Mapped { mapped, .. } if m == mapped), + None => matches!(self, Data::Mapped { .. }), + } + } + + fn orientation(&self) -> Orientation { + match self { + Data::Group { orientation, .. } => *orientation, + _ => panic!("Not a group"), + } + } + + fn add_window(&mut self, idx: usize) { + match self { + Data::Group { + sizes, + last_geometry, + orientation, + .. + } => { + let last_length = match orientation { + Orientation::Horizontal => last_geometry.size.h, + Orientation::Vertical => last_geometry.size.w, + }; + let equal_sizing = last_length / (sizes.len() as i32 + 1); // new window size + let remainder = last_length - equal_sizing; // size for the rest of the windowns + + for size in sizes.iter_mut() { + *size = ((*size as f64 / last_length as f64) * remainder as f64).round() as i32; + } + let used_size: i32 = sizes.iter().sum(); + let new_size = last_length - used_size; + + sizes.insert(idx, new_size); + } + Data::Mapped { .. } => panic!("Adding window to leaf?"), + } + } + + fn remove_window(&mut self, idx: usize) { + match self { + Data::Group { + sizes, + last_geometry, + orientation, + .. + } => { + let last_length = match orientation { + Orientation::Horizontal => last_geometry.size.h, + Orientation::Vertical => last_geometry.size.w, + }; + let old_size = sizes.remove(idx); + for size in sizes.iter_mut() { + *size += + ((old_size as f64 / last_length as f64) * (*size as f64)).round() as i32; + } + let used_size: i32 = sizes.iter().sum(); + let overflow = last_length - used_size; + if overflow != 0 { + *sizes.last_mut().unwrap() += overflow; + } + } + Data::Mapped { .. } => panic!("Added window to leaf?"), + } + } + + fn geometry(&self) -> &Rectangle { + match self { + Data::Group { last_geometry, .. } => last_geometry, + Data::Mapped { last_geometry, .. } => last_geometry, + } + } + + fn update_geometry(&mut self, geo: Rectangle) { + match self { + Data::Group { + orientation, + sizes, + last_geometry, + .. + } => { + let previous_length = match orientation { + Orientation::Horizontal => last_geometry.size.h, + Orientation::Vertical => last_geometry.size.w, + }; + let new_length = match orientation { + Orientation::Horizontal => geo.size.h, + Orientation::Vertical => geo.size.w, + }; + + sizes.iter_mut().for_each(|len| { + *len = (((*len as f64) / (previous_length as f64)) * (new_length as f64)) + .round() as i32; + }); + let sum: i32 = sizes.iter().sum(); + if sum < new_length { + *sizes.last_mut().unwrap() += new_length - sum; + } + *last_geometry = geo; + } + Data::Mapped { last_geometry, .. } => { + *last_geometry = geo; + } + } + } + + fn len(&self) -> usize { + match self { + Data::Group { sizes, .. } => sizes.len(), + Data::Mapped { .. } => 1, } } } @@ -66,79 +229,353 @@ impl TilingLayout { pub fn new() -> TilingLayout { TilingLayout { gaps: (0, 4), - trees: Vec::new(), - windows: HashSet::new(), + trees: HashMap::new(), } } } impl TilingLayout { - pub fn map_window<'a>( - &mut self, - space: &mut Space, - window: Window, - seat: &Seat, - focus_stack: impl Iterator + 'a, - ) { - self.map_window_internal(space, &window, Some(seat), Some(focus_stack)); - self.windows.insert(window); - self.refresh(space); + pub fn map_output(&mut self, output: &Output, location: Point) { + if !self.trees.contains_key(output) { + self.trees.insert( + OutputData { + output: output.clone(), + location, + }, + Tree::new(), + ); + } else { + let tree = self.trees.remove(output).unwrap(); + self.trees.insert( + OutputData { + output: output.clone(), + location, + }, + tree, + ); + } } - pub fn move_focus<'a>( + pub fn unmap_output(&mut self, output: &Output) { + if let Some(src) = self.trees.remove(output) { + // TODO: expects last remaining output + let (output, dst) = self.trees.iter_mut().next().unwrap(); + let orientation = match output.output.geometry().size { + x if x.w >= x.h => Orientation::Horizontal, + _ => Orientation::Vertical, + }; + TilingLayout::merge_trees(src, dst, orientation); + self.refresh() + } + } + + pub fn map<'a>( + &mut self, + window: CosmicMapped, + seat: &Seat, + focus_stack: impl Iterator + 'a, + ) { + let output = seat.active_output(); + self.map_internal(window, &output, Some(focus_stack)); + self.refresh(); + } + + fn map_internal<'a>( + &mut self, + window: impl Into, + output: &Output, + focus_stack: Option + 'a>, + ) { + let tree = self.trees.get_mut(output).expect("Output not mapped?"); + let window = window.into(); + let new_window = Node::new(Data::Mapped { + mapped: window.clone(), + last_geometry: Rectangle::from_loc_and_size((0, 0), (100, 100)), + }); + + let last_active = + focus_stack.and_then(|focus_stack| TilingLayout::last_active_window(tree, focus_stack)); + + let window_id = if let Some((_last_active_window, ref node_id)) = last_active { + let orientation = { + let window_size = tree.get(node_id).unwrap().data().geometry().size; + if window_size.w > window_size.h { + Orientation::Vertical + } else { + Orientation::Horizontal + } + }; + + let parent_id = tree.get(node_id).unwrap().parent().cloned(); + match parent_id { + Some(group_id) if tree.get(&group_id).unwrap().data().is_group() => { + TilingLayout::new_group(tree, &group_id, new_window, orientation) + } + None => { + // if there is no window, we couldn't have found the window_id. + // so if there is no parent, we have found a root, thus it is safe to unwrap. + let root_id = tree.root_node_id().cloned().unwrap(); + TilingLayout::new_group(tree, &root_id, new_window, orientation) + } + _ => panic!("Illegal tiling tree structure"), + } + } else { + // nothing? then we add to the root + if let Some(root_id) = tree.root_node_id().cloned() { + let orientation = { + let output_size = output.geometry().size; + if output_size.w > output_size.h { + Orientation::Vertical + } else { + Orientation::Horizontal + } + }; + TilingLayout::new_group(tree, &root_id, new_window, orientation) + } else { + tree.insert(new_window, InsertBehavior::AsRoot) + } + } + .unwrap(); + + *window.tiling_node_id.lock().unwrap() = Some(window_id); + } + + pub fn unmap(&mut self, window: &CosmicMapped) -> bool { + if self.unmap_window_internal(window) { + window.set_tiled(false); + self.refresh(); + true + } else { + false + } + } + + fn unmap_window_internal(&mut self, mapped: &CosmicMapped) -> bool { + if let Some(node_id) = mapped.tiling_node_id.lock().unwrap().as_ref() { + if let Some(tree) = self.trees.values_mut().find(|tree| { + tree.get(node_id) + .map(|node| node.data().is_mapped(Some(mapped))) + .unwrap_or(false) + }) { + let parent_id = tree + .get(&node_id) + .ok() + .and_then(|node| node.parent()) + .cloned(); + let position = parent_id.as_ref().and_then(|parent_id| { + tree.children_ids(&parent_id) + .unwrap() + .position(|id| id == node_id) + }); + let parent_parent_id = parent_id.as_ref().and_then(|parent_id| { + tree.get(parent_id) + .ok() + .and_then(|node| node.parent()) + .cloned() + }); + + // remove self + slog_scope::debug!("Remove window {:?}", mapped); + let _ = tree.remove_node(node_id.clone(), RemoveBehavior::DropChildren); + + // fixup parent node + match parent_id { + Some(id) => { + let position = position.unwrap(); + let group = tree.get_mut(&id).unwrap().data_mut(); + assert!(group.is_group()); + + if group.len() > 2 { + group.remove_window(position); + } else { + slog_scope::debug!("Removing Group"); + let other_child = + tree.children_ids(&id).unwrap().cloned().next().unwrap(); + let fork_pos = parent_parent_id.as_ref().and_then(|parent_id| { + tree.children_ids(parent_id).unwrap().position(|i| i == &id) + }); + let _ = tree.remove_node(id.clone(), RemoveBehavior::OrphanChildren); + tree.move_node( + &other_child, + parent_parent_id + .as_ref() + .map(|parent_id| MoveBehavior::ToParent(parent_id)) + .unwrap_or(MoveBehavior::ToRoot), + ) + .unwrap(); + if let Some(old_pos) = fork_pos { + tree.make_nth_sibling(&other_child, old_pos).unwrap(); + } + } + } + None => {} // root + } + + return true; + } + } + false + } + + pub fn output_for_element(&self, elem: &CosmicMapped) -> Option<&Output> { + self.mapped().find_map(|(o, m, _)| (m == elem).then_some(o)) + } + + pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { + if let Some(id) = elem.tiling_node_id.lock().unwrap().as_ref() { + if let Some(output) = self.output_for_element(elem) { + let (output_data, tree) = self.trees.get_key_value(output).unwrap(); + let node = tree.get(id).ok()?; + let data = node.data(); + assert!(data.is_mapped(Some(elem))); + let mut geo = *data.geometry(); + geo.loc += output_data.location; + return Some(geo); + } + } + None + } + + pub fn next_focus<'a>( &mut self, direction: FocusDirection, seat: &Seat, - space: &mut Space, - focus_stack: impl Iterator + 'a, - ) -> Option { - let output = super::output_from_seat(Some(seat), space); - let idx = space - .outputs() - .position(|o| Some(o) == output.as_ref()) - .unwrap_or(0); - let tree = TilingLayout::active_tree(&mut self.trees, idx); - if let Some(last_active) = TilingLayout::last_active_window(tree, focus_stack) { - let mut node_id = last_active; - while let Some((fork, child)) = TilingLayout::find_fork(tree, node_id) { - if let &Data::Fork { - ref orientation, .. - } = tree.get(&fork).unwrap().data() - { - // found a fork - // which child are we? - let first = tree.children_ids(&fork).unwrap().next() == Some(&child); - let focus_subtree = match (first, orientation, direction) { - (true, Orientation::Horizontal, FocusDirection::Down) - | (true, Orientation::Vertical, FocusDirection::Right) => { - tree.children_ids(&fork).unwrap().skip(1).next() - } - (false, Orientation::Horizontal, FocusDirection::Up) - | (false, Orientation::Vertical, FocusDirection::Left) => { - tree.children_ids(&fork).unwrap().next() - } - _ => None, // continue iterating - }; + focus_stack: impl Iterator + 'a, + ) -> Option { + let output = seat.active_output(); + let tree = self.trees.get_mut(&output).unwrap(); - if focus_subtree.is_some() { - let mut node_id = focus_subtree; - while node_id.is_some() - && !matches!( - tree.get(node_id.as_ref().unwrap()).unwrap().data(), - Data::Window(_) - ) - { - // TODO, if ndoe_id is a stack, we want to assign the active node instead of the first - node_id = tree.children_ids(node_id.as_ref().unwrap()).unwrap().next(); + // TODO: Rather use something like seat.current_keyboard_focus + // TODO https://github.com/Smithay/smithay/pull/777 + if let Some(last_active) = TilingLayout::last_active_window(tree, focus_stack) { + let (last_window, node_id) = last_active; + + // stacks may handle focus internally + if last_window.handle_focus(direction) { + return None; + } + + while let Some(group) = tree.get(&node_id).unwrap().parent() { + let child = node_id.clone(); + let group_data = tree.get(&group).unwrap().data(); + let main_orientation = group_data.orientation(); + assert!(group_data.is_group()); + + if direction == FocusDirection::Out { + return Some( + WindowGroup { + node: group.clone(), + output: output.downgrade(), + alive: match group_data { + &Data::Group { ref alive, .. } => Arc::downgrade(alive), + _ => unreachable!(), + }, } - if let Some(Data::Window(window)) = - node_id.and_then(|i| tree.get(&i).ok()).map(|n| n.data()) - { - return Some(window.clone()); + .into(), + ); + } + + // which child are we? + let idx = tree + .children_ids(&group) + .unwrap() + .position(|id| id == &child) + .unwrap(); + let len = group_data.len(); + + let focus_subtree = match (main_orientation, direction) { + (Orientation::Horizontal, FocusDirection::Down) + | (Orientation::Vertical, FocusDirection::Right) + if idx < (len - 1) => + { + tree.children_ids(&group).unwrap().skip(idx + 1).next() + } + (Orientation::Horizontal, FocusDirection::Up) + | (Orientation::Vertical, FocusDirection::Left) + if idx > 0 => + { + tree.children_ids(&group).unwrap().skip(idx - 1).next() + } + _ => None, // continue iterating + }; + + if focus_subtree.is_some() { + let mut node_id = focus_subtree; + while node_id.is_some() { + match tree.get(node_id.unwrap()).unwrap().data() { + Data::Group { orientation, .. } if orientation == &main_orientation => { + // if the group is layed out in the direction we care about, + // we can just use the first or last element (depending on the direction) + match direction { + FocusDirection::Down | FocusDirection::Right => { + node_id = tree + .children_ids(node_id.as_ref().unwrap()) + .unwrap() + .next(); + } + FocusDirection::Up | FocusDirection::Left => { + node_id = tree + .children_ids(node_id.as_ref().unwrap()) + .unwrap() + .last(); + } + _ => unreachable!(), + } + } + Data::Group { .. } => { + let center = { + let geo = tree.get(&child).unwrap().data().geometry(); + let mut point = geo.loc; + match direction { + FocusDirection::Down => { + point += Point::from((geo.size.w / 2, geo.size.h)) + } + FocusDirection::Up => point.x += geo.size.w, + FocusDirection::Left => point.y += geo.size.h / 2, + FocusDirection::Right => { + point += Point::from((geo.size.w, geo.size.h / 2)) + } + _ => unreachable!(), + }; + point.to_f64() + }; + + node_id = tree + .children_ids(node_id.as_ref().unwrap()) + .unwrap() + .min_by(|node1, node2| { + let distance = |candidate: &&NodeId| -> f64 { + let geo = + tree.get(candidate).unwrap().data().geometry(); + let mut point = geo.loc; + match direction { + FocusDirection::Up => { + point += + Point::from((geo.size.w / 2, geo.size.h)) + } + FocusDirection::Down => point.x += geo.size.w, + FocusDirection::Right => point.y += geo.size.h / 2, + FocusDirection::Left => { + point += + Point::from((geo.size.w, geo.size.h / 2)) + } + _ => unreachable!(), + }; + let point = point.to_f64(); + ((point.x - center.x).powi(2) + + (point.y - center.y).powi(2)) + .sqrt() + }; + + distance(node1).total_cmp(&distance(node2)) + }); + } + Data::Mapped { mapped, .. } => { + return Some(mapped.clone().into()); + } } } } - node_id = fork; } } @@ -149,92 +586,59 @@ impl TilingLayout { &mut self, new_orientation: Orientation, seat: &Seat, - space: &mut Space, - focus_stack: impl Iterator + 'a, + focus_stack: impl Iterator + 'a, ) { - let output = super::output_from_seat(Some(seat), space); - let idx = space - .outputs() - .position(|o| Some(o) == output.as_ref()) - .unwrap_or(0); - let tree = TilingLayout::active_tree(&mut self.trees, idx); - if let Some(last_active) = TilingLayout::last_active_window(tree, focus_stack) { - if let Some((fork, _child)) = TilingLayout::find_fork(tree, last_active) { - if let &mut Data::Fork { + let output = seat.active_output(); + let tree = self.trees.get_mut(&output).unwrap(); + if let Some((_, last_active)) = TilingLayout::last_active_window(tree, focus_stack) { + if let Some(group) = tree.get(&last_active).unwrap().parent().cloned() { + if let &mut Data::Group { ref mut orientation, + ref mut sizes, + ref last_geometry, .. - } = tree.get_mut(&fork).unwrap().data_mut() + } = tree.get_mut(&group).unwrap().data_mut() { + let previous_length = match orientation { + Orientation::Horizontal => last_geometry.size.h, + Orientation::Vertical => last_geometry.size.w, + }; + let new_length = match new_orientation { + Orientation::Horizontal => last_geometry.size.h, + Orientation::Vertical => last_geometry.size.w, + }; + + sizes.iter_mut().for_each(|len| { + *len = (((*len as f64) / (previous_length as f64)) * (new_length as f64)) + .round() as i32; + }); + let sum: i32 = sizes.iter().sum(); + if sum < new_length { + *sizes.last_mut().unwrap() += new_length - sum; + } + *orientation = new_orientation; } } } - self.refresh(space); + self.refresh(); } - pub fn refresh<'a>(&mut self, space: &mut Space) { - let active_outputs = space.outputs().count(); - if self.trees.len() > active_outputs { - for tree in self - .trees - .drain(active_outputs..self.trees.len()) - .collect::>() - .into_iter() - { - if let Some(root_id) = tree.root_node_id() { - for node in tree.traverse_pre_order(root_id).unwrap() { - if let Data::Window(window) = node.data() { - self.map_window_internal( - space, - window, - None, - Option::>::None, - ); - } - } - } - } - } - - let mut changed = false; - while let Some(dead_windows) = Some(TilingLayout::update_space_positions( - &mut self.trees, - space, - self.gaps, - )) - .filter(|v| !v.is_empty()) - { - for window in dead_windows { - self.unmap_window_internal(&window); - } - changed = true; - } - if changed { - for window in &self.windows { - update_reactive_popups(space, window); - } + pub fn refresh<'a>(&mut self) { + let dead_windows = self + .mapped() + .map(|(_, w, _)| w.clone()) + .filter(|w| !w.alive()) + .collect::>(); + for dead_window in dead_windows.iter() { + self.unmap_window_internal(&dead_window); } + TilingLayout::update_space_positions(&mut self.trees, self.gaps); } - pub fn unmap_window(&mut self, space: &mut Space, window: &Window) { - self.unmap_window_internal(window); - space.unmap_window(window); - #[allow(irrefutable_let_patterns)] - if let Kind::Xdg(xdg) = &window.toplevel() { - xdg.with_pending_state(|state| { - state.states.unset(XdgState::TiledLeft); - state.states.unset(XdgState::TiledRight); - state.states.unset(XdgState::TiledTop); - state.states.unset(XdgState::TiledBottom); - }); - } - self.windows.remove(window); - self.refresh(space); - } - + /* pub fn resize_request( - state: &mut State, - window: &Window, + window: &CosmicWindow, seat: &Seat, serial: Serial, start_data: PointerGrabStartData, @@ -293,179 +697,30 @@ impl TilingLayout { } } } - - fn active_tree<'a>(trees: &'a mut Vec>, output: usize) -> &'a mut Tree { - while trees.len() <= output { - trees.push(Tree::new()) - } - &mut trees[output] - } + */ fn last_active_window<'a>( tree: &mut Tree, - mut focus_stack: impl Iterator, - ) -> Option { - let last_active = focus_stack - .find_map(|window| tree.root_node_id() + mut focus_stack: impl Iterator, + ) -> Option<(CosmicMapped, NodeId)> { + focus_stack + .find_map(|mapped| tree.root_node_id() .and_then(|root| tree.traverse_pre_order_ids(root).unwrap() - .find(|id| matches!(tree.get(id).map(|n| n.data()), Ok(Data::Window(w)) if w == window)) - ) - ); - - last_active - } - - fn find_fork(tree: &mut Tree, mut node_id: NodeId) -> Option<(NodeId, NodeId)> { - while let Some(parent_id) = tree.get(&node_id).unwrap().parent().cloned() { - if let &Data::Fork { .. } = tree.get(&parent_id).unwrap().data() { - return Some((parent_id, node_id)); - } - node_id = parent_id; - } - None - } - - fn map_window_internal<'a>( - &mut self, - space: &mut Space, - window: &Window, - seat: Option<&Seat>, - focus_stack: Option + 'a>, - ) { - let output = super::output_from_seat(seat, space); - let idx = space - .outputs() - .position(|o| Some(o) == output.as_ref()) - .unwrap_or(0); - let tree = TilingLayout::active_tree(&mut self.trees, idx); - let new_window = Node::new(Data::Window(window.clone())); - - let last_active = focus_stack.and_then(|mut iter| - iter.find_map(|window| tree.root_node_id() - .and_then(|root| tree.traverse_pre_order_ids(root).unwrap() - .find(|id| matches!(tree.get(id).map(|n| n.data()), Ok(Data::Window(w)) if w == window)) - ) + .find(|id| matches!(tree.get(id).map(|n| n.data()), Ok(Data::Mapped { mapped: m, .. }) if m == mapped)) + ).map(|id| (mapped.clone(), id)) ) - ); - let window_id = if let Some(ref node_id) = last_active { - let parent_id = tree.get(node_id).unwrap().parent().cloned(); - if let Some(stack_id) = - parent_id.filter(|id| matches!(tree.get(id).unwrap().data(), Data::Stack { .. })) - { - // we add to the stack - let window_id = tree - .insert(new_window, InsertBehavior::UnderNode(&stack_id)) - .unwrap(); - if let Data::Stack { - ref mut len, - ref mut active, - } = tree.get_mut(&stack_id).unwrap().data_mut() - { - *active = *len; - *len += 1; - } - Ok(window_id) - } else { - // we create a new fork - TilingLayout::new_fork(tree, node_id, new_window) - } - } else { - // nothing? then we add to the root - if let Some(root_id) = tree.root_node_id().cloned() { - TilingLayout::new_fork(tree, &root_id, new_window) - } else { - tree.insert(new_window, InsertBehavior::AsRoot) - } - } - .unwrap(); - - { - let user_data = window.user_data(); - let window_info = WindowInfo { - node: window_id.clone(), - output: idx, - }; - // insert or update - if !user_data.insert_if_missing(|| RefCell::new(window_info.clone())) { - *user_data.get::>().unwrap().borrow_mut() = window_info; - } - } } - fn unmap_window_internal(&mut self, window: &Window) { - if let Some(info) = window.user_data().get::>() { - let output = info.borrow().output; - let tree = TilingLayout::active_tree(&mut self.trees, output); - - let node_id = info.borrow().node.clone(); - let parent_id = tree - .get(&node_id) - .ok() - .and_then(|node| node.parent()) - .cloned(); - let parent_parent_id = parent_id.as_ref().and_then(|parent_id| { - tree.get(parent_id) - .ok() - .and_then(|node| node.parent()) - .cloned() - }); - - // remove self - slog_scope::debug!("Remove window {:?}", window); - let _ = tree.remove_node(node_id.clone(), RemoveBehavior::DropChildren); - - // fixup parent node - match parent_id { - Some(id) if matches!(tree.get(&id).unwrap().data(), Data::Fork { .. }) => { - slog_scope::debug!("Removing Fork"); - let other_child = tree.children_ids(&id).unwrap().cloned().next().unwrap(); - let fork_pos = parent_parent_id.as_ref().and_then(|parent_id| { - tree.children_ids(parent_id).unwrap().position(|i| i == &id) - }); - let _ = tree.remove_node(id.clone(), RemoveBehavior::OrphanChildren); - tree.move_node( - &other_child, - parent_parent_id - .as_ref() - .map(|parent_id| MoveBehavior::ToParent(parent_id)) - .unwrap_or(MoveBehavior::ToRoot), - ) - .unwrap(); - if let Some(old_pos) = fork_pos { - tree.make_nth_sibling(&other_child, old_pos).unwrap(); - } - } - Some(id) if matches!(tree.get(&id).unwrap().data(), Data::Stack { .. }) => { - if tree.children_ids(&id).unwrap().count() == 0 { - slog_scope::debug!("Removing Stack"); - // remove stack - let _ = tree.remove_node(id.clone(), RemoveBehavior::DropChildren); - // TODO now we need to untangle the parent_parent - // So we should REFACTOR this unmap function to not only work with windows - } else { - // fixup stack values - if let Data::Stack { - ref mut active, - ref mut len, - } = tree.get_mut(&id).unwrap().data_mut() - { - *len -= 1; - *active = std::cmp::max(*active, *len - 1); - } - } - } - None => {} // root - _ => unreachable!(), - } - } - } - - fn new_fork( + fn new_group( tree: &mut Tree, old_id: &NodeId, new: Node, + orientation: Orientation, ) -> Result { - let new_fork = Node::new(Data::fork()); + let new_group = Node::new(Data::new_group( + orientation, + Rectangle::from_loc_and_size((0, 0), (100, 100)), + )); let old = tree.get(old_id)?; let parent_id = old.parent().cloned(); let pos = parent_id.as_ref().and_then(|parent_id| { @@ -474,9 +729,9 @@ impl TilingLayout { .position(|id| id == old_id) }); - let fork_id = tree + let group_id = tree .insert( - new_fork, + new_group, if let Some(parent) = parent_id.as_ref() { InsertBehavior::UnderNode(parent) } else { @@ -485,30 +740,21 @@ impl TilingLayout { ) .unwrap(); - tree.move_node(old_id, MoveBehavior::ToParent(&fork_id)) + tree.move_node(old_id, MoveBehavior::ToParent(&group_id)) .unwrap(); // keep position if let Some(old_pos) = pos { - tree.make_nth_sibling(&fork_id, old_pos).unwrap(); + tree.make_nth_sibling(&group_id, old_pos).unwrap(); } - tree.insert(new, InsertBehavior::UnderNode(&fork_id)) + tree.insert(new, InsertBehavior::UnderNode(&group_id)) } - fn update_space_positions( - trees: &mut Vec>, - space: &mut Space, - gaps: (i32, i32), - ) -> Vec { - let mut dead_windows = Vec::new(); + fn update_space_positions(trees: &mut HashMap>, gaps: (i32, i32)) { let (outer, inner) = gaps; - for (idx, output) in space - .outputs() - .cloned() - .enumerate() - .collect::>() - .into_iter() + for (output, tree) in trees + .iter_mut() + .map(|(output_data, tree)| (&output_data.output, tree)) { - let tree = TilingLayout::active_tree(trees, idx); if let Some(root) = tree.root_node_id() { let mut stack = Vec::new(); @@ -519,98 +765,179 @@ impl TilingLayout { geo.loc.y += outer; geo.size.w -= outer * 2; geo.size.h -= outer * 2; + + if tree.get(root).unwrap().data().geometry() == geo { + continue; + } } - for node in tree.traverse_pre_order(root).unwrap() { + for node_id in tree + .traverse_pre_order_ids(root) + .unwrap() + .collect::>() + .into_iter() + { + let node = tree.get_mut(&node_id).unwrap(); let geo = stack.pop().unwrap_or(geo); - match node.data() { - Data::Fork { orientation, ratio } => { - if let Some(geo) = geo { - match orientation { - Orientation::Horizontal => { - let top_size = ( - geo.size.w, - ((geo.size.h as f64) * ratio.load(Ordering::SeqCst)) - .ceil() - as i32, - ); + if let Some(geo) = geo { + let data = node.data_mut(); + data.update_geometry(geo); + match data { + Data::Group { + orientation, sizes, .. + } => match orientation { + Orientation::Horizontal => { + let mut previous = 0; + for size in sizes { stack.push(Some(Rectangle::from_loc_and_size( - (geo.loc.x, geo.loc.y + top_size.1), - (geo.size.w, geo.size.h - top_size.1), - ))); - stack.push(Some(Rectangle::from_loc_and_size( - geo.loc, top_size, - ))); - } - Orientation::Vertical => { - let left_size = ( - ((geo.size.w as f64) * ratio.load(Ordering::SeqCst)) - .ceil() - as i32, - geo.size.h, - ); - stack.push(Some(Rectangle::from_loc_and_size( - (geo.loc.x + left_size.0, geo.loc.y), - (geo.size.w - left_size.0, geo.size.h), - ))); - stack.push(Some(Rectangle::from_loc_and_size( - geo.loc, left_size, + (geo.loc.x, geo.loc.y + previous), + (geo.size.w, *size), ))); + previous += *size; } } - } else { - stack.push(None); - stack.push(None); - } - } - Data::Stack { active, len } => { - for i in 0..*len { - if i == *active { - stack.push(geo); - } else { - stack.push(None); - } - } - } - Data::Window(window) => { - if window.alive() { - if let Some(geo) = geo { - #[allow(irrefutable_let_patterns)] - if let Kind::Xdg(xdg) = &window.toplevel() { - if xdg.current_state().states.contains(XdgState::Fullscreen) - || xdg.with_pending_state(|pending| { - pending.states.contains(XdgState::Fullscreen) - }) - { - continue; - } - xdg.with_pending_state(|state| { - state.size = Some( - (geo.size.w - inner * 2, geo.size.h - inner * 2) - .into(), - ); - state.states.set(XdgState::TiledLeft); - state.states.set(XdgState::TiledRight); - state.states.set(XdgState::TiledTop); - state.states.set(XdgState::TiledBottom); - }); - xdg.send_configure(); + Orientation::Vertical => { + let mut previous = 0; + for size in sizes { + stack.push(Some(Rectangle::from_loc_and_size( + (geo.loc.x + previous, geo.loc.y), + (*size, geo.size.h), + ))); + previous += *size; } - space.map_window( - &window, - (geo.loc.x + inner, geo.loc.y + inner), - None, - false, + } + }, + Data::Mapped { mapped, .. } => { + if !mapped.is_fullscreen() { + mapped.set_tiled(true); + mapped.set_size( + (geo.size.w - inner * 2, geo.size.h - inner * 2).into(), ); + mapped.configure(); } - } else { - dead_windows.push(window.clone()); } } + } else if node.data().is_group() { + stack.push(None); + stack.push(None); } } } } - dead_windows + } + + pub fn mapped(&self) -> impl Iterator)> { + self.trees + .iter() + .flat_map(|(output_data, tree)| { + if let Some(root) = tree.root_node_id() { + Some( + tree.traverse_pre_order(root) + .unwrap() + .filter(|node| node.data().is_mapped(None)) + .map(|node| match node.data() { + Data::Mapped { + mapped, + last_geometry, + .. + } => ( + &output_data.output, + mapped, + output_data.location + last_geometry.loc, + ), + _ => unreachable!(), + }), + ) + } else { + None + } + }) + .flatten() + } + + pub fn windows(&self) -> impl Iterator)> + '_ { + self.mapped().flat_map(|(output, mapped, loc)| { + mapped + .windows() + .map(move |(w, p)| (output.clone(), w, p + loc)) + }) + } + + pub fn merge(&mut self, other: TilingLayout) { + for (output_data, src) in other.trees { + let mut dst = self.trees.entry(output_data.clone()).or_default(); + let orientation = match output_data.output.geometry().size { + x if x.w >= x.h => Orientation::Horizontal, + _ => Orientation::Vertical, + }; + TilingLayout::merge_trees(src, &mut dst, orientation); + } + self.refresh(); + } + + fn merge_trees(src: Tree, dst: &mut Tree, orientation: Orientation) { + if let Some(root_id) = src.root_node_id() { + let mut stack = Vec::new(); + + let root_node = src.get(root_id).unwrap(); + let new_node = Node::new(root_node.data().clone()); + let into_node_id = match dst.root_node_id().cloned() { + Some(root) => TilingLayout::new_group(dst, &root, new_node, orientation), + None => dst.insert(new_node, InsertBehavior::AsRoot), + } + .unwrap(); + + stack.push((root_id.clone(), into_node_id)); + while let Some((src_id, dst_id)) = stack.pop() { + for child_id in src.children_ids(&src_id).unwrap() { + let src_node = src.get(&child_id).unwrap(); + let new_node = Node::new(src_node.data().clone()); + let new_child_id = dst + .insert(new_node, InsertBehavior::UnderNode(&dst_id)) + .unwrap(); + stack.push((child_id.clone(), new_child_id)); + } + } + } else { + *dst = src; + } + } + + pub fn render_output( + &self, + output: &Output, + ) -> Result>, OutputNotMapped> + where + R: Renderer + ImportAll, + ::TextureId: 'static, + { + let output_scale = output.current_scale().fractional_scale(); + let int_scale = output.current_scale().integer_scale(); + + if !self.trees.contains_key(output) { + return Err(OutputNotMapped); + } + + Ok(self + .mapped() + .flat_map(|(o, mapped, loc)| { + if o == output { + Some((mapped, loc)) + } else { + None + } + }) + .flat_map(|(mapped, loc)| { + mapped.render_elements::>( + loc.to_physical(int_scale), + Scale::from(output_scale), + ) + }) + .collect::>()) } } + +render_elements! { + pub TilingRenderElement where R: ImportAll; + Window=CosmicMappedRenderElement, +} diff --git a/src/shell/mod.rs b/src/shell/mod.rs index b4f7671f..491147b7 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1,5 +1,7 @@ -use std::{cell::Cell, mem::MaybeUninit}; +use serde::{Deserialize, Serialize}; +use std::{cell::Cell, collections::HashMap}; +use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::State as WState; use smithay::{ desktop::{layer_map_for_output, LayerSurface, PopupManager, Window, WindowSurfaceType}, input::{pointer::MotionEvent, Seat}, @@ -17,11 +19,8 @@ use smithay::{ }, }; -use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::State as WState; - use crate::{ config::{Config, WorkspaceMode as ConfigMode}, - //state::ClientState, utils::prelude::*, wayland::protocols::{ toplevel_info::ToplevelInfoState, @@ -33,19 +32,23 @@ use crate::{ }, }; -pub const MAX_WORKSPACES: usize = 10; +mod element; pub mod focus; -pub mod grabs; +//pub mod grabs; pub mod layout; mod workspace; pub use self::workspace::*; +use self::{ + element::{CosmicMapped, CosmicWindow}, + focus::target::KeyboardFocusTarget, + layout::{floating::FloatingLayout, tiling::TilingLayout}, +}; pub struct Shell { pub popups: PopupManager, - pub spaces: [Workspace; MAX_WORKSPACES], pub outputs: Vec, - pub workspace_mode: WorkspaceMode, - pub shell_mode: ShellMode, + pub workspaces: WorkspaceMode, + pub workspace_amount: WorkspaceAmount, pub floating_default: bool, pub pending_windows: Vec<(Window, Seat)>, pub pending_layers: Vec<(LayerSurface, Output, Seat)>, @@ -58,28 +61,245 @@ pub struct Shell { pub workspace_state: WorkspaceState, } -#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Debug)] +pub struct WorkspaceSet { + active: usize, + group: WorkspaceGroupHandle, + workspaces: Vec, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub enum WorkspaceAmount { + Dynamic, + Static(u8), +} + +fn create_workspace( + state: &mut WorkspaceUpdateGuard<'_, State>, + group_handle: &WorkspaceGroupHandle, + active: bool, +) -> Workspace { + let workspace_handle = state.create_workspace(&group_handle).unwrap(); + if active { + state.add_workspace_state(&workspace_handle, WState::Active); + } + init_workspace_handle(state, 0, &workspace_handle); + Workspace::new(workspace_handle) +} + +impl WorkspaceSet { + fn new(state: &mut WorkspaceUpdateGuard<'_, State>, amount: WorkspaceAmount) -> WorkspaceSet { + let group_handle = state.create_workspace_group(); + + let workspaces = match amount { + WorkspaceAmount::Dynamic => { + vec![create_workspace(state, &group_handle, true)] + } + WorkspaceAmount::Static(len) => (0..len) + .map(|i| create_workspace(state, &group_handle, i == 0)) + .collect(), + }; + + WorkspaceSet { + active: 0, + group: group_handle, + workspaces, + } + } + + fn activate(&mut self, idx: usize, state: &mut WorkspaceUpdateGuard<'_, State>) { + if idx < self.workspaces.len() && self.active != idx { + 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); + self.active = idx; + } + } + + fn refresh( + &mut self, + amount: WorkspaceAmount, + state: &mut WorkspaceState, + toplevel_info: &mut ToplevelInfoState, + ) { + match amount { + WorkspaceAmount::Dynamic => self.ensure_last_empty(state), + WorkspaceAmount::Static(len) => self.ensure_static(len as usize, state, toplevel_info), + } + self.workspaces[self.active].refresh(); + } + + fn ensure_last_empty(&mut self, state: &mut WorkspaceState) { + // add empty at the end, if necessary + if self.workspaces.last().unwrap().windows().next().is_some() { + self.workspaces + .push(create_workspace(&mut state.update(), &self.group, false)); + } + + let len = self.workspaces.len(); + let mut keep = vec![true; len]; + + // remove empty workspaces in between, if they are not active + 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 { + state.update().remove_workspace(workspace.handle); + keep[i] = false; + } + } + + let mut iter = keep.iter(); + self.workspaces.retain(|_| *iter.next().unwrap()); + } + + fn ensure_static( + &mut self, + amount: usize, + state: &mut WorkspaceState, + toplevel_info: &mut ToplevelInfoState, + ) { + if amount < self.workspaces.len() { + let mut state = state.update(); + // 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(); + + for workspace in overflow { + 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); + last_space + .fullscreen + .extend(workspace.fullscreen.into_iter()); + state.remove_workspace(workspace.handle); + } + + last_space.refresh(); + } else if amount > self.workspaces.len() { + let mut state = state.update(); + // add empty ones + while amount > self.workspaces.len() { + self.workspaces + .push(create_workspace(&mut state, &self.group, false)); + } + } + } +} + +#[derive(Debug)] pub enum WorkspaceMode { - OutputBound, - Global { - active: usize, - group: WorkspaceGroupHandle, - }, + OutputBound(HashMap), + Global(WorkspaceSet), } -pub enum ShellMode { - Normal, - Resize, - Adjust, -} +impl WorkspaceMode { + pub fn new( + config: crate::config::WorkspaceMode, + amount: WorkspaceAmount, + state: &mut WorkspaceUpdateGuard<'_, State>, + ) -> WorkspaceMode { + match config { + crate::config::WorkspaceMode::Global => { + WorkspaceMode::Global(WorkspaceSet::new(state, amount)) + } + crate::config::WorkspaceMode::OutputBound => WorkspaceMode::OutputBound(HashMap::new()), + } + } -#[derive(Debug, Clone)] -pub struct OutputBoundState { - pub active: Cell, - group: Cell, -} + pub fn get(&self, num: usize, output: &Output) -> Option<&Workspace> { + match self { + WorkspaceMode::Global(set) => set.workspaces.get(num), + WorkspaceMode::OutputBound(sets) => { + sets.get(output).and_then(|set| set.workspaces.get(num)) + } + } + } -const UNINIT_SPACE: MaybeUninit = MaybeUninit::uninit(); + pub fn get_mut(&mut self, num: usize, output: &Output) -> Option<&mut Workspace> { + match self { + WorkspaceMode::Global(set) => set.workspaces.get_mut(num), + WorkspaceMode::OutputBound(sets) => sets + .get_mut(output) + .and_then(|set| set.workspaces.get_mut(num)), + } + } + + pub fn active(&self, output: &Output) -> &Workspace { + match self { + WorkspaceMode::Global(set) => &set.workspaces[set.active], + WorkspaceMode::OutputBound(sets) => { + let set = sets.get(output).unwrap(); + &set.workspaces[set.active] + } + } + } + + pub fn active_mut(&mut self, output: &Output) -> &mut Workspace { + match self { + WorkspaceMode::Global(set) => &mut set.workspaces[set.active], + WorkspaceMode::OutputBound(sets) => { + let set = sets.get_mut(output).unwrap(); + &mut set.workspaces[set.active] + } + } + } + + pub fn active_num(&self, output: &Output) -> usize { + match self { + WorkspaceMode::Global(set) => set.active, + WorkspaceMode::OutputBound(sets) => { + let set = sets.get(output).unwrap(); + set.active + } + } + } + + pub fn spaces(&self) -> impl Iterator { + match self { + WorkspaceMode::Global(set) => { + Box::new(set.workspaces.iter()) as Box> + } + WorkspaceMode::OutputBound(sets) => { + Box::new(sets.values().flat_map(|set| set.workspaces.iter())) + } + } + } + + pub fn spaces_for_output(&self, output: &Output) -> impl Iterator { + match self { + WorkspaceMode::Global(set) => { + Box::new(set.workspaces.iter()) as Box> + } + WorkspaceMode::OutputBound(sets) => Box::new( + sets.get(output) + .into_iter() + .flat_map(|set| set.workspaces.iter()), + ), + } + } + + pub fn spaces_mut(&mut self) -> impl Iterator { + match self { + WorkspaceMode::Global(set) => { + Box::new(set.workspaces.iter_mut()) as Box> + } + WorkspaceMode::OutputBound(sets) => { + Box::new(sets.values_mut().flat_map(|set| set.workspaces.iter_mut())) + } + } + } +} impl Shell { pub fn new(config: &Config, dh: &DisplayHandle) -> Self { @@ -106,32 +326,20 @@ impl Shell { |_| true, ); - let mut spaces = unsafe { - let mut spaces = [UNINIT_SPACE; MAX_WORKSPACES]; - for (idx, space) in spaces.iter_mut().enumerate() { - *space = MaybeUninit::new(Workspace::new( - idx as u8, - std::mem::zeroed(), /* Will be initialized by init_mode */ - )); - } - std::mem::transmute(spaces) - }; - let mode = init_mode( - &config.static_conf.workspace_mode, - None, - &[], - &mut workspace_state, - &mut spaces, + let amount = config.static_conf.workspace_amount; + let mode = WorkspaceMode::new( + config.static_conf.workspace_mode, + config.static_conf.workspace_amount, + &mut workspace_state.update(), ); let floating_default = config.static_conf.floating_default; Shell { popups: PopupManager::new(None), - spaces, outputs: Vec::new(), - workspace_mode: mode, + workspaces: mode, + workspace_amount: amount, floating_default, - shell_mode: ShellMode::Normal, pending_windows: Vec::new(), pending_layers: Vec::new(), @@ -145,65 +353,28 @@ impl Shell { } pub fn add_output(&mut self, output: &Output) { - let was_empty = self.outputs.is_empty(); self.outputs.push(output.clone()); let mut state = self.workspace_state.update(); - match self.workspace_mode { - WorkspaceMode::OutputBound => { - let idx = self - .spaces - .iter() - .position(|x| x.space.outputs().next().is_none()) - .expect("More then 10 outputs?"); - - remap_output( - output, - &mut self.spaces, - None, - idx, - Point::from((0, 0)), - &mut self.toplevel_info_state, - ); - let mut workspace = &mut self.spaces[idx]; - - let group = state.create_workspace_group(); - state.add_group_output(&group, output); - state.remove_workspace(workspace.handle); - let handle = init_workspace_handle(&mut state, &group, &mut workspace); - state.add_workspace_state(&handle, WState::Active); - - let output_state = OutputBoundState { - active: Cell::new(workspace.idx as usize), - group: Cell::new(group), - }; - - if was_empty { - for workspace in self.spaces.iter_mut().skip(1) { - init_workspace_handle(&mut state, &group, workspace); - } + match &mut self.workspaces { + WorkspaceMode::OutputBound(sets) => { + // TODO: Restore previously assigned workspaces, if possible! + if !sets.contains_key(output) { + sets.insert( + output.clone(), + WorkspaceSet::new(&mut state, self.workspace_amount), + ); } - - if !output - .user_data() - .insert_if_missing(|| output_state.clone()) - { - let existing_state = output.user_data().get::().unwrap(); - existing_state.active.set(output_state.active.get()); - existing_state.group.set(output_state.group.get()); + for workspace in &mut sets.get_mut(output).unwrap().workspaces { + workspace.map_output(output, (0, 0).into()); } } - WorkspaceMode::Global { active, group } => { - state.add_group_output(&group, output); - - remap_output( - output, - &mut self.spaces, - None, - active, - output.current_location(), - &mut self.toplevel_info_state, - ); + WorkspaceMode::Global(set) => { + // TODO: Restore any window positions from previous outputs ??? + state.add_group_output(&set.group, output); + for workspace in &mut set.workspaces { + workspace.map_output(output, output.current_location()); + } } } } @@ -212,221 +383,267 @@ impl Shell { let mut state = self.workspace_state.update(); self.outputs.retain(|o| o != output); - match self.workspace_mode { - WorkspaceMode::OutputBound => { - let output_state = output.user_data().get::().unwrap(); - remap_output( - output, - &mut self.spaces, - output_state.active.get(), - None, - None, - &mut self.toplevel_info_state, - ); + match &mut self.workspaces { + WorkspaceMode::OutputBound(sets) => { + if let Some(set) = sets.remove(output) { + // 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. + if let Some(new_output) = self.outputs.get(0) { + let new_set = sets.get_mut(new_output).unwrap(); + let workspace_group = new_set.group; + for mut workspace in set.workspaces { + // update workspace protocol state + state.remove_workspace(workspace.handle); + let workspace_handle = + state.create_workspace(&workspace_group).unwrap(); + init_workspace_handle( + &mut state, + new_set.workspaces.len() as u8, + &workspace_handle, + ); + workspace.handle = workspace_handle; - // reassign workspaces to a different output - let new_group = self - .outputs - .iter() - .next() - .map(|o| o.user_data().get::().unwrap().group.get()); - for mut workspace in self.spaces.iter_mut() { - if state - .workspace_belongs_to_group(&output_state.group.get(), &workspace.handle) - { - state.remove_workspace(workspace.handle); - if let Some(new_group) = new_group { - init_workspace_handle(&mut state, &new_group, &mut workspace); + // update mapping + workspace.map_output(new_output, (0, 0).into()); + workspace.unmap_output(output); + workspace.refresh(); + + new_set.workspaces.push(workspace); } + state.remove_workspace_group(set.group); + std::mem::drop(state); + self.refresh(); // cleans up excess of workspaces and empty workspaces } + // if there is no output, we are going to quit anyway, just drop the workspace set } - - // destroy workspace group - state.remove_workspace_group(output_state.group.get()); } - WorkspaceMode::Global { active, group } => { - state.remove_group_output(&group, output); - - remap_output( - output, - &mut self.spaces, - active, - None, - None, - &mut self.toplevel_info_state, - ); + WorkspaceMode::Global(set) => { + state.remove_group_output(&set.group, output); + for workspace in &mut set.workspaces { + workspace.unmap_output(output); + workspace.refresh(); + } } }; } pub fn refresh_outputs(&mut self) { - if let WorkspaceMode::Global { active, .. } = self.workspace_mode { - let workspace = &mut self.spaces[active]; - for output in self.outputs.iter() { - workspace - .space - .map_output(output, output.current_location()); - } - } else { - for output in self.outputs.iter() { - let active = output - .user_data() - .get::() - .unwrap() - .active - .get(); - let workspace = &mut self.spaces[active]; - workspace.space.map_output(output, (0, 0)); + if let WorkspaceMode::Global(set) = &mut self.workspaces { + for workspace in &mut set.workspaces { + for output in self.outputs.iter() { + workspace.map_output(output, output.current_location()); + } } } } pub fn set_mode(&mut self, mode: ConfigMode) { - match (&mut self.workspace_mode, mode) { - (WorkspaceMode::OutputBound, ConfigMode::Global) => { - let new_active = 0; - init_mode( - &mode, - Some(&WorkspaceMode::OutputBound), - &self.outputs, - &mut self.workspace_state, - &mut self.spaces, - ); + let mut state = self.workspace_state.update(); + + match (&mut self.workspaces, mode) { + (dst @ WorkspaceMode::OutputBound(_), ConfigMode::Global) => { + // rustc should really be able to infer that this doesn't need an if. + let sets = if let &mut WorkspaceMode::OutputBound(ref mut sets) = dst { + sets + } else { + unreachable!() + }; + + // in this case we have to merge our sets, preserving placing of windows as nicely as possible + let mut new_set = WorkspaceSet::new(&mut state, WorkspaceAmount::Static(0)); + + // lets construct an iterator of all the pairs of workspaces we have to merge + // we first split of the part of the workspaces that contain the currently active one + let mut second_half = sets + .iter_mut() + .map(|(output, set)| (output.clone(), set.workspaces.split_off(set.active))) + .collect::>(); + + let mut first_half = std::iter::repeat(()) + // we continuously pop the last elements from the first half and group them together. + .map(|_| { + sets.iter_mut() + .flat_map(|(o, w)| w.workspaces.pop().map(|w| (o.clone(), w))) + .collect::>() + }) + // we stop once there is no workspace anymore in the entire set + .filter(|vec| !vec.is_empty()) + .fuse() + .collect::>(); + // we reverse those then to get the proper order + first_half.reverse(); + + let mergers = first_half + .into_iter() + // we need to know, which is supposed to be active and we loose that info by chaining, so lets add a bool + .map(|w| (w, false)) + .chain( + (0..) + // here we continuously remove the first element + .map(|i| { + ( + second_half + .iter_mut() + .flat_map(|&mut (ref o, ref mut w)| { + if !w.is_empty() { + Some((o.clone(), w.remove(0))) + } else { + None + } + }) + .collect::>(), + i == 0, + ) + }) + .filter(|(vec, _)| !vec.is_empty()) + .fuse(), + ); + + for (i, (workspaces, active)) in mergers.into_iter().enumerate() { + // and then we can merge each vector into one and put that into our new set. + let workspace_handle = state.create_workspace(&new_set.group).unwrap(); + init_workspace_handle(&mut state, i as u8, &workspace_handle); + + let mut new_workspace = Workspace::new(workspace_handle); + for output in self.outputs.iter() { + new_workspace.map_output(output, output.current_location()); + } + new_workspace.tiling_enabled = workspaces.iter().any(|(_, w)| w.tiling_enabled); + + for (_output, workspace) in workspaces.into_iter() { + for toplevel in workspace.windows() { + self.toplevel_info_state + .toplevel_leave_workspace(&toplevel, &workspace.handle); + self.toplevel_info_state + .toplevel_enter_workspace(&toplevel, &new_workspace.handle); + } + new_workspace.tiling_layer.merge(workspace.tiling_layer); + new_workspace.floating_layer.merge(workspace.floating_layer); + new_workspace + .fullscreen + .extend(workspace.fullscreen.into_iter()); + state.remove_workspace(workspace.handle); + } + + if active { + new_set.active = new_set.workspaces.len(); + } + new_set.workspaces.push(new_workspace); + } + + for group in sets.values().map(|set| set.group) { + state.remove_workspace_group(group); + } + + *dst = WorkspaceMode::Global(new_set); + } + (dst @ WorkspaceMode::Global(_), ConfigMode::OutputBound) => { + // rustc should really be able to infer that this doesn't need an if. + let set = if let &mut WorkspaceMode::Global(ref mut set) = dst { + set + } else { + unreachable!() + }; + + // split workspaces apart, preserving window positions relative to their outputs + let mut sets = HashMap::new(); for output in &self.outputs { - let old_active = output - .user_data() - .get::() - .unwrap() - .active - .get(); - remap_output( - output, - &mut self.spaces, - old_active, - new_active, - output.current_location(), - &mut self.toplevel_info_state, + sets.insert( + output.clone(), + WorkspaceSet::new(&mut state, WorkspaceAmount::Static(0)), ); } - } - (x @ WorkspaceMode::Global { .. }, ConfigMode::OutputBound) => { - // inits OutputBoundState if it not exists - init_mode( - &mode, - Some(x), - &self.outputs, - &mut self.workspace_state, - &mut self.spaces, - ); - if let WorkspaceMode::Global { ref active, .. } = x { + for (i, workspace) in set.workspaces.drain(..).enumerate() { for output in &self.outputs { - let new_active = output - .user_data() - .get::() - .unwrap() - .active - .get(); - remap_output( - output, - &mut self.spaces, - *active, - new_active, - Point::from((0, 0)), - &mut self.toplevel_info_state, - ); + // copy over everything and then remove other outputs to preserve state + let new_set = sets.get_mut(output).unwrap(); + let new_workspace_handle = state.create_workspace(&new_set.group).unwrap(); + init_workspace_handle(&mut state, i as u8, &new_workspace_handle); + + let mut old_tiling_layer = workspace.tiling_layer.clone(); + let mut new_floating_layer = FloatingLayout::new(); + let mut new_tiling_layer = TilingLayout::new(); + + for element in workspace.mapped() { + for (toplevel, _) in element.windows() { + self.toplevel_info_state + .toplevel_leave_workspace(&toplevel, &workspace.handle); + } + + if workspace + .floating_layer + .most_overlapped_output_for_element(element) + .as_ref() + == Some(output) + { + if let Some(mut old_mapped_loc) = + workspace.floating_layer.space.element_location(element) + { + let old_output_geo = workspace + .floating_layer + .space + .output_geometry(output) + .unwrap(); + old_mapped_loc -= old_output_geo.loc; + new_floating_layer.map_internal( + element.clone(), + output, + Some(old_mapped_loc), + ); + } + } else { + old_tiling_layer.unmap(element); + } + } + + new_floating_layer.map_output(output, (0, 0).into()); + new_tiling_layer.map_output(output, (0, 0).into()); + new_tiling_layer.merge(old_tiling_layer); + + let mut new_workspace = Workspace { + tiling_layer: new_tiling_layer, + floating_layer: new_floating_layer, + tiling_enabled: workspace.tiling_enabled, + fullscreen: workspace + .fullscreen + .iter() + .filter(|(key, _)| *key == output) + .map(|(o, w)| (o.clone(), w.clone())) + .collect(), + ..Workspace::new(new_workspace_handle) + }; + for toplevel in new_workspace.windows() { + self.toplevel_info_state + .toplevel_enter_workspace(&toplevel, &new_workspace_handle); + } + new_workspace.refresh(); + + new_set.workspaces.push(new_workspace); + new_set.active = set.active; } + state.remove_workspace(workspace.handle); } + state.remove_workspace_group(set.group); + + *dst = WorkspaceMode::OutputBound(sets); } _ => {} } + + std::mem::drop(state); + self.refresh(); // get rid of empty workspaces and enforce potential maximum } - pub fn activate( - &mut self, - seat: &Seat, - output: &Output, - idx: usize, - ) -> Option { - if idx > MAX_WORKSPACES { - return None; - } - - match self.workspace_mode { - WorkspaceMode::OutputBound => { - // if the workspace is active on a different output, move the cursor over - for output in self.outputs.iter().filter(|o| o != &output) { - if output - .user_data() - .get::() - .unwrap() - .active - .get() - == idx - { - let geometry = output.geometry(); - set_active_output(seat, output); - return Some(MotionEvent { - location: Point::::from(( - geometry.loc.x + (geometry.size.w / 2), - geometry.loc.y + (geometry.size.h / 2), - )) - .to_f64(), - serial: SERIAL_COUNTER.next_serial(), - time: 0, - }); - } - } - - // else we exchange the workspace on the current output - let output_state = output.user_data().get::().unwrap(); - let old_active = output_state.active.get(); - if idx != old_active { - let mut state = self.workspace_state.update(); - output_state.active.set(idx); - - if !state.workspace_belongs_to_group( - &output_state.group.get(), - &self.spaces[idx].handle, - ) { - state.remove_workspace(self.spaces[idx].handle); - init_workspace_handle( - &mut state, - &output_state.group.get(), - &mut self.spaces[idx], - ); - } - - state.remove_workspace_state(&self.spaces[old_active].handle, WState::Active); - state.add_workspace_state(&self.spaces[idx].handle, WState::Active); - - std::mem::drop(state); - remap_output( - output, - &mut self.spaces, - old_active, - idx, - Point::from((0, 0)), - &mut self.toplevel_info_state, - ); + pub fn activate(&mut self, output: &Output, idx: usize) -> Option { + match &mut self.workspaces { + WorkspaceMode::OutputBound(sets) => { + if let Some(set) = sets.get_mut(output) { + set.activate(idx, &mut self.workspace_state.update()); } } - WorkspaceMode::Global { ref mut active, .. } => { - let old = *active; - *active = idx; - - let mut state = self.workspace_state.update(); - for output in &self.outputs { - remap_output( - output, - &mut self.spaces, - old, - idx, - output.current_location(), - &mut self.toplevel_info_state, - ); - } - state.remove_workspace_state(&self.spaces[old].handle, WState::Active); - state.add_workspace_state(&self.spaces[idx].handle, WState::Active); + WorkspaceMode::Global(set) => { + set.activate(idx, &mut self.workspace_state.update()); } } @@ -434,32 +651,22 @@ impl Shell { } pub fn active_space(&self, output: &Output) -> &Workspace { - match &self.workspace_mode { - WorkspaceMode::OutputBound => { - let active = output - .user_data() - .get::() - .unwrap() - .active - .get(); - &self.spaces[active] + match &self.workspaces { + WorkspaceMode::OutputBound(sets) => { + let set = sets.get(output).unwrap(); + &set.workspaces[set.active] } - WorkspaceMode::Global { active, .. } => &self.spaces[*active], + WorkspaceMode::Global(set) => &set.workspaces[set.active], } } pub fn active_space_mut(&mut self, output: &Output) -> &mut Workspace { - match &self.workspace_mode { - WorkspaceMode::OutputBound => { - let active = output - .user_data() - .get::() - .unwrap() - .active - .get(); - &mut self.spaces[active] + match &mut self.workspaces { + WorkspaceMode::OutputBound(sets) => { + let set = sets.get_mut(output).unwrap(); + &mut set.workspaces[set.active] } - WorkspaceMode::Global { active, .. } => &mut self.spaces[*active], + WorkspaceMode::Global(set) => &mut set.workspaces[set.active], } } @@ -475,39 +682,31 @@ impl Shell { Some(output) => { Box::new(std::iter::once(output.clone())) as Box> } - None => Box::new( - self.spaces - .iter() - .filter_map(|w| { - if let Some(window) = - w.space.window_for_surface(surface, WindowSurfaceType::ALL) - { - Some(w.space.outputs_for_window(&window).into_iter()) - } else { - None - } - }) - .flatten(), - ), + None => Box::new(self.workspaces.spaces().flat_map(|w| { + w.mapped() + .find(|e| e.has_surface(surface, WindowSurfaceType::ALL)) + .into_iter() + .flat_map(|e| w.outputs_for_element(e)) + })), } } - pub fn space_for_window(&self, surface: &WlSurface) -> Option<&Workspace> { - self.spaces.iter().find(|workspace| { - workspace - .space - .window_for_surface(surface, WindowSurfaceType::ALL) - .is_some() - }) + pub fn element_for_surface(&self, surface: &WlSurface) -> Option<&CosmicMapped> { + self.workspaces + .spaces() + .find_map(|w| w.element_for_surface(surface)) } - pub fn space_for_window_mut(&mut self, surface: &WlSurface) -> Option<&mut Workspace> { - self.spaces.iter_mut().find(|workspace| { - workspace - .space - .window_for_surface(surface, WindowSurfaceType::ALL) - .is_some() - }) + 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)) } pub fn outputs(&self) -> impl Iterator { @@ -527,66 +726,59 @@ impl Shell { .unwrap_or_else(|| Rectangle::from_loc_and_size((0, 0), (0, 0))) } - pub fn space_relative_output_geometry( + pub fn map_global_to_space( &self, global_loc: impl Into>, output: &Output, ) -> Point { - match self.workspace_mode { - WorkspaceMode::Global { .. } => global_loc.into(), - WorkspaceMode::OutputBound => { + match self.workspaces { + WorkspaceMode::Global(_) => global_loc.into(), + WorkspaceMode::OutputBound(_) => { let p = global_loc.into().to_f64() - output.current_location().to_f64(); (C::from_f64(p.x), C::from_f64(p.y)).into() } } } - pub fn refresh(&mut self, dh: &DisplayHandle) { + pub fn map_space_to_global( + &self, + space_loc: impl Into>, + output: &Output, + ) -> Point { + match self.workspaces { + WorkspaceMode::Global(_) => space_loc.into(), + WorkspaceMode::OutputBound(_) => { + let p = space_loc.into().to_f64() + output.current_location().to_f64(); + (C::from_f64(p.x), C::from_f64(p.y)).into() + } + } + } + + pub fn refresh(&mut self) { self.popups.cleanup(); - match &self.workspace_mode { - WorkspaceMode::OutputBound => { - for output in &self.outputs { - let active = output - .user_data() - .get::() - .unwrap() - .active - .get(); - let workspace = &mut self.spaces[active]; - workspace.refresh(dh); - if workspace.space.windows().next().is_none() - && !self - .workspace_state - .workspace_states(&workspace.handle) - .map(|mut i| i.any(|s| s == &WState::Hidden)) - .unwrap_or(true) - { - self.workspace_state - .update() - .add_workspace_state(&workspace.handle, WState::Hidden); - } + + match &mut self.workspaces { + WorkspaceMode::OutputBound(sets) => { + for set in sets.values_mut() { + set.refresh( + self.workspace_amount, + &mut self.workspace_state, + &mut self.toplevel_info_state, + ); } } - WorkspaceMode::Global { active, .. } => { - let workspace = &mut self.spaces[*active]; - workspace.refresh(dh); - if workspace.space.windows().next().is_none() - && !self - .workspace_state - .workspace_states(&workspace.handle) - .map(|mut i| i.any(|s| s == &WState::Hidden)) - .unwrap_or(true) - { - self.workspace_state - .update() - .add_workspace_state(&workspace.handle, WState::Hidden); - } - } - }; + WorkspaceMode::Global(set) => set.refresh( + self.workspace_amount, + &mut self.workspace_state, + &mut self.toplevel_info_state, + ), + } + for output in &self.outputs { let mut map = layer_map_for_output(output); - map.cleanup(dh); + map.cleanup(); } + self.toplevel_info_state .refresh(Some(&self.workspace_state)); } @@ -600,54 +792,34 @@ impl Shell { .position(|(w, _)| w == window) .unwrap(); let (window, seat) = state.common.shell.pending_windows.remove(pos); - let surface = window.toplevel().wl_surface().clone(); - let workspace = match &state.common.shell.workspace_mode { - WorkspaceMode::OutputBound => { - let active = output - .user_data() - .get::() - .unwrap() - .active - .get(); - &mut state.common.shell.spaces[active] - } - WorkspaceMode::Global { active, .. } => &mut state.common.shell.spaces[*active], - }; - state - .common - .shell - .workspace_state - .update() - .remove_workspace_state(&workspace.handle, WState::Hidden); - state - .common - .shell - .toplevel_info_state - .toplevel_enter_workspace(&window, &workspace.handle); + let workspace = state.common.shell.workspaces.active_mut(output); state .common .shell .toplevel_info_state .toplevel_enter_output(&window, &output); + state + .common + .shell + .toplevel_info_state + .toplevel_enter_workspace(&window, &workspace.handle); + + let mapped = CosmicMapped::from(CosmicWindow::from(window.clone())); if layout::should_be_floating(&window) || state.common.shell.floating_default { - workspace - .floating_layer - .map_window(&mut workspace.space, window, &seat, None); + workspace.floating_layer.map(mapped.clone(), &seat, None); } else { - let focus_stack = workspace.focus_stack(&seat); - workspace.tiling_layer.map_window( - &mut workspace.space, - window, - &seat, - focus_stack.iter(), - ); + let focus_stack = workspace.focus_stack.get(&seat); + workspace + .tiling_layer + .map(mapped.clone(), &seat, focus_stack.iter()); } - Shell::set_focus(state, Some(&surface), &seat, None); + Shell::set_focus(state, Some(&KeyboardFocusTarget::from(mapped)), &seat, None); - for window in state.common.shell.active_space(output).space.windows() { - state.common.shell.update_reactive_popups(window); + let active_space = state.common.shell.active_space(output); + for mapped in active_space.mapped() { + state.common.shell.update_reactive_popups(mapped); } } @@ -661,9 +833,8 @@ impl Shell { .unwrap(); let (layer_surface, output, seat) = state.common.shell.pending_layers.remove(pos); - let surface = layer_surface.wl_surface(); let wants_focus = { - with_states(surface, |states| { + with_states(layer_surface.wl_surface(), |states| { let state = states.cached_state.current::(); matches!(state.layer, Layer::Top | Layer::Overlay) && state.keyboard_interactivity != KeyboardInteractivity::None @@ -671,194 +842,78 @@ impl Shell { }; let mut map = layer_map_for_output(&output); - map.map_layer(&state.common.display_handle, &layer_surface) - .unwrap(); + map.map_layer(&layer_surface).unwrap(); if wants_focus { - Shell::set_focus(state, Some(surface), &seat, None) + Shell::set_focus(state, Some(&layer_surface.into()), &seat, None) } } pub fn move_current_window(&mut self, seat: &Seat, output: &Output, idx: usize) { - if idx > MAX_WORKSPACES { + if idx == self.workspaces.active_num(output) { return; } - let workspace = match &self.workspace_mode { - WorkspaceMode::OutputBound => { - let active = output - .user_data() - .get::() - .unwrap() - .active - .get(); - &mut self.spaces[active] - } - WorkspaceMode::Global { active, .. } => &mut self.spaces[*active], - }; - if idx == workspace.idx as usize { - return; - } + let old_workspace = self.workspaces.active_mut(output); + let maybe_window = old_workspace.focus_stack.get(seat).last().cloned(); + if let Some(mapped) = maybe_window { + let was_floating = old_workspace.floating_layer.unmap(&mapped); + let was_tiling = old_workspace.tiling_layer.unmap(&mapped); + assert!(was_floating != was_tiling); - let maybe_window = workspace.focus_stack(seat).last(); - if let Some(window) = maybe_window { - let mut workspace_state = self.workspace_state.update(); - workspace - .floating_layer - .unmap_window(&mut workspace.space, &window); - workspace - .tiling_layer - .unmap_window(&mut workspace.space, &window); - self.toplevel_info_state - .toplevel_leave_workspace(&window, &workspace.handle); - if workspace.space.windows().next().is_none() { - workspace_state.add_workspace_state(&workspace.handle, WState::Hidden); + for (toplevel, _) in mapped.windows() { + self.toplevel_info_state + .toplevel_leave_workspace(&toplevel, &old_workspace.handle); + } + let elements = old_workspace.mapped().cloned().collect::>(); + std::mem::drop(old_workspace); + for mapped in elements.into_iter() { + self.update_reactive_popups(&mapped); } - let new_workspace = &mut self.spaces[idx]; - workspace_state.remove_workspace_state(&new_workspace.handle, WState::Hidden); - self.toplevel_info_state - .toplevel_enter_workspace(&window, &new_workspace.handle); - let focus_stack = new_workspace.focus_stack(&seat); - if layout::should_be_floating(&window) { - new_workspace.floating_layer.map_window( - &mut new_workspace.space, - window, - &seat, - None, - ); + let new_workspace = self.workspaces.get_mut(idx, output).unwrap(); // checked above + let focus_stack = new_workspace.focus_stack.get(&seat); + if was_floating { + new_workspace + .floating_layer + .map(mapped.clone(), &seat, None); } else { - new_workspace.tiling_layer.map_window( - &mut new_workspace.space, - window, - &seat, - focus_stack.iter(), + new_workspace + .tiling_layer + .map(mapped.clone(), &seat, focus_stack.iter()); + } + for (toplevel, _) in mapped.windows() { + self.toplevel_info_state + .toplevel_enter_workspace(&toplevel, &new_workspace.handle); + } + + let mut workspace_state = self.workspace_state.update(); + workspace_state.remove_workspace_state(&new_workspace.handle, WState::Hidden); + } + } + + pub fn update_reactive_popups(&self, mapped: &CosmicMapped) { + if let Some(workspace) = self.space_for(mapped) { + let element_loc = workspace.element_geometry(mapped).unwrap().loc; + for (toplevel, offset) in mapped.windows() { + let window_geo_offset = toplevel.geometry().loc; + update_reactive_popups( + &toplevel, + element_loc + offset + window_geo_offset, + self.outputs.iter(), ); } } - - for window in self.active_space(output).space.windows() { - self.update_reactive_popups(window); - } - for window in self.spaces[idx].space.windows() { - self.update_reactive_popups(window); - } - } -} - -fn init_mode( - config_mode: &ConfigMode, - old_mode: Option<&WorkspaceMode>, - outputs: &[Output], - state: &mut WorkspaceState, - workspaces: &mut [Workspace; MAX_WORKSPACES], -) -> WorkspaceMode { - let mut state = state.update(); - - // cleanup - for workspace in workspaces.iter_mut() { - state.remove_workspace(workspace.handle); - } - - match old_mode { - Some(WorkspaceMode::Global { group, .. }) => state.remove_workspace_group(group.clone()), - Some(WorkspaceMode::OutputBound) => { - for output in outputs { - if let Some(old_state) = output.user_data().get::() { - state.remove_workspace_group(old_state.group.get()); - } - } - } - _ => {} - }; - - // set the new state (especially cosmic_workspace state) - match config_mode { - ConfigMode::Global => { - let group = state.create_workspace_group(); - for output in outputs { - state.add_group_output(&group, output) - } - for workspace in workspaces.iter_mut() { - init_workspace_handle(&mut state, &group, workspace); - } - state.add_workspace_state(&workspaces[0].handle, WState::Active); - state.remove_workspace_state(&workspaces[0].handle, WState::Hidden); - WorkspaceMode::Global { active: 0, group } - } - ConfigMode::OutputBound => { - for (i, output) in outputs.iter().enumerate() { - let group = state.create_workspace_group(); - state.add_group_output(&group, output); - - let workspace = workspaces.get_mut(i).expect("More then ten workspaces?!?"); - let handle = init_workspace_handle(&mut state, &group, workspace); - state.add_workspace_state(&handle, WState::Active); - state.remove_workspace_state(&handle, WState::Hidden); - - let output_state = OutputBoundState { - active: Cell::new(i), - group: Cell::new(group), - }; - let map = output.user_data(); - if !map.insert_if_missing(|| output_state) { - let old_state = map.get::().unwrap(); - old_state.active.set(i); - old_state.group.set(group); - } - } - if !outputs.is_empty() { - for workspace in workspaces.iter_mut().skip(outputs.iter().count()) { - let group = outputs[0] - .user_data() - .get::() - .unwrap() - .group - .get(); - init_workspace_handle(&mut state, &group, workspace); - } - } - WorkspaceMode::OutputBound - } } } fn init_workspace_handle<'a>( state: &mut WorkspaceUpdateGuard<'a, State>, - group: &WorkspaceGroupHandle, - workspace: &mut Workspace, -) -> WorkspaceHandle { - let handle = state.create_workspace(&group).unwrap(); - state.set_workspace_capabilities(&handle, [WorkspaceCapabilities::Activate].into_iter()); - state.set_workspace_name(&handle, format!("{}", workspace.idx + 1)); - state.set_workspace_coordinates(&handle, [Some(workspace.idx as u32), None, None]); - if workspace.space.windows().next().is_none() { - state.add_workspace_state(&handle, WState::Hidden); - } - workspace.handle = handle.clone(); - handle -} - -fn remap_output( - output: &Output, - spaces: &mut [Workspace], - old: impl Into>, - new: impl Into>, - pos: impl Into>>, - info_state: &mut ToplevelInfoState, + idx: u8, + handle: &WorkspaceHandle, ) { - if let Some(old) = old.into() { - let old_space = &mut spaces[old].space; - old_space.unmap_output(output); - for window in old_space.windows() { - info_state.toplevel_leave_output(window, output); - } - } - if let Some(new) = new.into() { - let new_space = &mut spaces[new].space; - new_space.map_output(output, pos.into().expect("new requires pos")); - for window in new_space.windows() { - info_state.toplevel_enter_output(window, output); - } - } + state.set_workspace_capabilities(&handle, [WorkspaceCapabilities::Activate].into_iter()); + state.set_workspace_name(&handle, format!("{}", idx + 1)); + state.set_workspace_coordinates(&handle, [Some(idx as u32), None, None]); + state.add_workspace_state(&handle, WState::Hidden); } diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 0fbe52d7..00271300 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1,79 +1,179 @@ use crate::{ - shell::layout::{floating::FloatingLayout, tiling::TilingLayout}, + shell::{ + element::CosmicWindow, + layout::{floating::FloatingLayout, tiling::TilingLayout}, + }, state::State, + utils::prelude::*, wayland::protocols::workspace::WorkspaceHandle, }; +use indexmap::IndexSet; use smithay::{ - desktop::{Kind, Space, Window, WindowSurfaceType}, + backend::renderer::{ + element::{surface::WaylandSurfaceRenderElement, AsRenderElements}, + ImportAll, Renderer, + }, + desktop::{ + layer_map_for_output, space::SpaceElement, Kind, LayerSurface, Space, Window, + WindowSurfaceType, + }, input::{pointer::GrabStartData as PointerGrabStartData, Seat}, - output::Output, + output::{Output, WeakOutput}, reexports::{ wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge}, wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle}, }, - utils::{IsAlive, Serial}, + render_elements, + utils::{IsAlive, Logical, Point, Rectangle, Scale, Serial}, + wayland::shell::wlr_layer::Layer, }; -use std::collections::HashMap; +use std::{collections::HashMap, time::Duration}; +use super::{ + element::CosmicMapped, + focus::{FocusStack, FocusStackMut}, + layout::{floating::FloatingRenderElement, tiling::TilingRenderElement}, +}; + +#[derive(Debug)] pub struct Workspace { - pub idx: u8, - pub space: Space, pub tiling_layer: TilingLayout, pub floating_layer: FloatingLayout, - tiling_enabled: bool, - pub fullscreen: HashMap, + pub tiling_enabled: bool, + pub fullscreen: HashMap, pub handle: WorkspaceHandle, + pub focus_stack: FocusStacks, } +#[derive(Debug, Default)] +pub struct FocusStacks(HashMap, IndexSet>); + impl Workspace { - pub fn new(idx: u8, handle: WorkspaceHandle) -> Workspace { + pub fn new(handle: WorkspaceHandle) -> Workspace { Workspace { - idx, - space: Space::new(None), tiling_layer: TilingLayout::new(), floating_layer: FloatingLayout::new(), tiling_enabled: true, fullscreen: HashMap::new(), handle, + focus_stack: FocusStacks::default(), } } - pub fn refresh(&mut self, dh: &DisplayHandle) { - let outputs = self.space.outputs().collect::>(); - let dead_output_windows = self - .fullscreen - .iter() - .filter(|(name, _)| !outputs.iter().any(|o| o.name() == **name)) - .map(|(_, w)| w) - .cloned() - .collect::>(); - for window in dead_output_windows { - self.unfullscreen_request(&window); - } + pub fn refresh(&mut self) { self.fullscreen.retain(|_, w| w.alive()); - self.floating_layer.refresh(&mut self.space); - self.tiling_layer.refresh(&mut self.space); - self.space.refresh(dh); + self.floating_layer.refresh(); + self.tiling_layer.refresh(); } - pub fn maximize_request(&mut self, window: &Window, output: &Output) { + 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) { + 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) + }) + } + + pub fn outputs_for_element(&self, elem: &CosmicMapped) -> impl Iterator { + 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) -> 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, + ) -> Option<(&CosmicMapped, Point)> { + 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 + .is_in_input_region(&test_point) + .then_some((mapped, loc)) + }) + }) + } + + pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { + let space = &self.floating_layer.space; + let outputs = space.outputs().collect::>(); + 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) { if self.fullscreen.values().any(|w| w == window) { return; } if self.floating_layer.windows.contains(window) { self.floating_layer - .maximize_request(&mut self.space, window, output); + .maximize_request(, window, output); } } - - pub fn unmaximize_request(&mut self, window: &Window) { + 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 - .unmaximize_request(&mut self.space, window); + .unmaximize_request(window); } } @@ -101,9 +201,10 @@ impl Workspace { TilingLayout::resize_request(state, &window, seat, serial, start_data, edges) } } + */ pub fn fullscreen_request(&mut self, window: &Window, output: &Output) { - if self.fullscreen.contains_key(&output.name()) { + if self.fullscreen.contains_key(output) { return; } @@ -123,7 +224,7 @@ impl Workspace { }); xdg.send_configure(); - self.fullscreen.insert(output.name(), window.clone()); + self.fullscreen.insert(output.clone(), window.clone()); } } @@ -135,8 +236,8 @@ impl Workspace { state.states.unset(xdg_toplevel::State::Fullscreen); state.size = None; }); - self.floating_layer.refresh(&mut self.space); - self.tiling_layer.refresh(&mut self.space); + self.floating_layer.refresh(); + self.tiling_layer.refresh(); xdg.send_configure(); } self.fullscreen.retain(|_, w| w != window); @@ -144,7 +245,7 @@ impl Workspace { } pub fn fullscreen_toggle(&mut self, window: &Window, output: &Output) { - if self.fullscreen.contains_key(&output.name()) { + if self.fullscreen.contains_key(output) { self.unfullscreen_request(window) } else { self.fullscreen_request(window, output) @@ -152,26 +253,33 @@ impl Workspace { } pub fn get_fullscreen(&self, output: &Output) -> Option<&Window> { - if !self.space.outputs().any(|o| o == output) { - return None; - } - self.fullscreen.get(&output.name()).filter(|w| w.alive()) + self.fullscreen.get(output).filter(|w| w.alive()) } pub fn toggle_tiling(&mut self, seat: &Seat) { if self.tiling_enabled { - for window in self.tiling_layer.windows.clone().into_iter() { - self.tiling_layer.unmap_window(&mut self.space, &window); - self.floating_layer - .map_window(&mut self.space, window, seat, None); + for window in self + .tiling_layer + .mapped() + .map(|(_, m, _)| m.clone()) + .collect::>() + .into_iter() + { + self.tiling_layer.unmap(&window); + self.floating_layer.map(window, seat, None); } self.tiling_enabled = false; } else { - let focus_stack = self.focus_stack(seat); - for window in self.floating_layer.windows.clone().into_iter() { - self.floating_layer.unmap_window(&mut self.space, &window); - self.tiling_layer - .map_window(&mut self.space, window, seat, focus_stack.iter()) + let focus_stack = self.focus_stack.get(seat); + for window in self + .floating_layer + .mapped() + .cloned() + .collect::>() + .into_iter() + { + self.floating_layer.unmap(&window); + self.tiling_layer.map(window, seat, focus_stack.iter()) } self.tiling_enabled = true; } @@ -179,18 +287,157 @@ impl Workspace { pub fn toggle_floating_window(&mut self, seat: &Seat) { if self.tiling_enabled { - if let Some(window) = self.focus_stack(seat).iter().next().cloned() { - if self.tiling_layer.windows.contains(&window) { - self.tiling_layer.unmap_window(&mut self.space, &window); - self.floating_layer - .map_window(&mut self.space, window, seat, None); - } else if self.floating_layer.windows.contains(&window) { - let focus_stack = self.focus_stack(seat); - self.floating_layer.unmap_window(&mut self.space, &window); - self.tiling_layer - .map_window(&mut self.space, window, seat, focus_stack.iter()) + 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()) } } } } + + pub fn mapped(&self) -> impl Iterator { + self.floating_layer + .mapped() + .chain(self.tiling_layer.mapped().map(|(_, w, _)| w)) + } + + pub fn windows(&self) -> impl Iterator + '_ { + self.floating_layer + .windows() + .chain(self.tiling_layer.windows().map(|(_, w, _)| w)) + } + + pub fn render_output( + &self, + output: &Output, + ) -> Result>, OutputNotMapped> + where + R: Renderer + ImportAll, + ::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::::render_elements::>( + surface, + loc.to_physical_precise_round(output_scale), + Scale::from(output_scale), + ) + }), + ); + + // fullscreen window + render_elements.extend(AsRenderElements::::render_elements::< + WorkspaceRenderElement, + >(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::::render_elements::>( + surface, + loc.to_physical_precise_round(output_scale), + Scale::from(output_scale), + ) + }), + ); + + lower + }; + + // floating surfaces + render_elements.extend( + self.floating_layer + .render_output::(output)? + .into_iter() + .map(WorkspaceRenderElement::from), + ); + + //tiling surfaces + render_elements.extend( + self.tiling_layer + .render_output::(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::::render_elements::>( + 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) -> FocusStack<'a> { + FocusStack(self.0.get(seat)) + } + + pub fn get_mut<'a>(&'a mut self, seat: &Seat) -> FocusStackMut<'a> { + FocusStackMut(self.0.entry(seat.clone()).or_default()) + } +} + +pub struct OutputNotMapped; + +render_elements! { + pub WorkspaceRenderElement where R: ImportAll; + Wayland=WaylandSurfaceRenderElement, + Floating=FloatingRenderElement, + Tiling=TilingRenderElement, } diff --git a/src/state.rs b/src/state.rs index 3eaf3bbe..eeb69cb5 100644 --- a/src/state.rs +++ b/src/state.rs @@ -7,8 +7,8 @@ use crate::{ shell::Shell, utils::prelude::*, wayland::protocols::{ - drm::WlDrmState, export_dmabuf::ExportDmabufState, - output_configuration::OutputConfigurationState, workspace::WorkspaceClientState, + drm::WlDrmState, output_configuration::OutputConfigurationState, + workspace::WorkspaceClientState, }, }; use smithay::{ @@ -22,7 +22,6 @@ use smithay::{ Display, DisplayHandle, }, }, - utils::{Buffer, Size}, wayland::{ compositor::CompositorState, data_device::DataDeviceState, dmabuf::DmabufState, keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitState, output::OutputManagerState, @@ -71,8 +70,8 @@ pub struct Common { pub shell: Shell, pub dirty_flag: Arc, - pub seats: Vec>, - pub last_active_seat: Seat, + seats: Vec>, + last_active_seat: Option>, pub start_time: Instant, pub should_stop: bool, @@ -85,7 +84,6 @@ pub struct Common { pub compositor_state: CompositorState, pub data_device_state: DataDeviceState, pub dmabuf_state: DmabufState, - pub export_dmabuf_state: ExportDmabufState, pub keyboard_shortcuts_inhibit_state: KeyboardShortcutsInhibitState, pub output_state: OutputManagerState, pub output_configuration_state: OutputConfigurationState, @@ -201,88 +199,6 @@ impl BackendData { _ => unreachable!("No backend was initialized"), } } - - pub fn offscreen_for_output( - &mut self, - output: &Output, - state: &mut Common, - ) -> anyhow::Result<(Vec, Size)> { - use crate::backend::render::{render_output, AsGles2Renderer, CustomElem}; - use anyhow::Context; - use smithay::backend::renderer::{ImportAll, Renderer}; - use smithay::desktop::space::RenderElement; - use smithay::{ - backend::{ - drm::NodeType, - renderer::{gles2::Gles2Renderbuffer, Bind, ExportMem, Offscreen}, - }, - utils::Rectangle, - }; - - fn capture( - gpu: Option, - renderer: &mut R, - output: &Output, - state: &mut Common, - ) -> anyhow::Result<(Vec, Size)> - where - E: std::error::Error + Send + Sync + 'static, - T: Clone + 'static, - R: Renderer - + ImportAll - + AsGles2Renderer - + Offscreen - + Bind - + ExportMem, - CustomElem: RenderElement, - { - let size = output - .geometry() - .size - .to_f64() - .to_buffer( - output.current_scale().fractional_scale(), - output.current_transform().into(), - ) - .to_i32_round(); - let buffer = Offscreen::::create_buffer(renderer, size)?; - renderer.bind(buffer)?; - render_output( - gpu.as_ref(), - renderer, - 0, - state, - output, - false, - #[cfg(feature = "debug")] - None, - ) - .map_err(|err| anyhow::anyhow!("Failed to render output: {:?}", err))?; // lifetime issue, grrr - let mapping = renderer.copy_framebuffer(Rectangle::from_loc_and_size((0, 0), size))?; - let data = Vec::from(renderer.map_texture(&mapping)?); - - Ok((data, size)) - } - - match self { - BackendData::Winit(winit) => capture(None, winit.backend.renderer(), output, state), - BackendData::X11(x11) => capture(None, &mut x11.renderer, output, state), - BackendData::Kms(kms) => { - let node = kms - .target_node_for_output(output) - .unwrap_or(kms.primary) - .node_with_type(NodeType::Render) - .with_context(|| "Unable to find node")??; - capture( - Some(node), - &mut kms.api.renderer::(&node, &node)?, - output, - state, - ) - } - BackendData::Unset => unreachable!(), - } - } } impl State { @@ -297,22 +213,16 @@ impl State { let compositor_state = CompositorState::new::(dh, None); let data_device_state = DataDeviceState::new::(dh, None); let dmabuf_state = DmabufState::new(); - let export_dmabuf_state = ExportDmabufState::new::( - dh, - //|client| client.get_data::().unwrap().privileged, - |_| true, - ); let keyboard_shortcuts_inhibit_state = KeyboardShortcutsInhibitState::new::(dh); let output_state = OutputManagerState::new_with_xdg_output::(dh); let output_configuration_state = OutputConfigurationState::new(dh, |_| true); let primary_selection_state = PrimarySelectionState::new::(dh, None); let shm_state = ShmState::new::(dh, vec![], None); - let mut seat_state = SeatState::::new(); + let seat_state = SeatState::::new(); let viewporter_state = ViewporterState::new::(dh, None); let wl_drm_state = WlDrmState; let shell = Shell::new(&config, dh); - let initial_seat = crate::input::add_seat(dh, &mut seat_state, &config, "seat-0".into()); #[cfg(not(feature = "debug"))] let dirty_flag = Arc::new(AtomicBool::new(false)); @@ -330,8 +240,8 @@ impl State { shell, dirty_flag, - seats: vec![initial_seat.clone()], - last_active_seat: initial_seat, + seats: Vec::new(), + last_active_seat: None, start_time: Instant::now(), should_stop: false, @@ -354,7 +264,6 @@ impl State { compositor_state, data_device_state, dmabuf_state, - export_dmabuf_state, shm_state, seat_state, keyboard_shortcuts_inhibit_state, @@ -376,10 +285,9 @@ impl State { match std::env::var("COSMIC_RENDER_AUTO_ASSIGN").map(|val| val.to_lowercase()) { Ok(val) if val == "y" || val == "yes" || val == "true" => Some( kms_state - .target_node_for_output(&active_output( - &self.common.last_active_seat, - &self.common, - )) + .target_node_for_output( + &self.common.last_active_seat().active_output(), + ) .unwrap_or(kms_state.primary), ), _ => Some(kms_state.primary), @@ -415,6 +323,46 @@ impl State { } } +impl Common { + pub fn add_seat(&mut self, seat: Seat) { + if self.seats.is_empty() { + self.last_active_seat = Some(seat.clone()); + } + self.seats.push(seat); + } + + pub fn remove_seat(&mut self, seat: &Seat) { + self.seats.retain(|s| s != seat); + if self.seats.is_empty() { + self.last_active_seat = None; + } else if self.last_active_seat() == seat { + self.last_active_seat = Some(self.seats[0].clone()); + } + } + + pub fn seats(&self) -> impl Iterator> { + self.seats.iter() + } + + pub fn last_active_seat(&self) -> &Seat { + self.last_active_seat.as_ref().expect("No seat?") + } + + pub fn send_frames(&self, output: &Output) { + let workspace = self.shell.active_space(output); + workspace.mapped().for_each(|mapped| { + if workspace.outputs_for_element(mapped).any(|o| &o == output) { + let window = mapped.active_window(); + window.send_frame(self.start_time.elapsed().as_millis() as u32) + } + }); + let map = smithay::desktop::layer_map_for_output(output); + for layer_surface in map.layers() { + layer_surface.send_frame(self.start_time.elapsed().as_millis() as u32) + } + } +} + #[cfg(feature = "debug")] impl Fps { const WINDOW_SIZE: usize = 100; diff --git a/src/utils/prelude.rs b/src/utils/prelude.rs index 55936e98..20ed2571 100644 --- a/src/utils/prelude.rs +++ b/src/utils/prelude.rs @@ -4,7 +4,6 @@ use smithay::{ output::Output, utils::{Logical, Rectangle, Transform}, }; -use std::cell::RefCell; pub use crate::shell::{Shell, Workspace}; pub use crate::state::{Common, State}; @@ -32,34 +31,25 @@ impl OutputExt for Output { pub trait SeatExt { fn id(&self) -> usize; + + fn active_output(&self) -> Output; + fn set_active_output(&self, output: &Output); } impl SeatExt for Seat { fn id(&self) -> usize { self.user_data().get::().unwrap().0 } -} -pub fn active_output(seat: &Seat, state: &Common) -> Output { - seat.user_data() - .get::() - .map(|x| x.0.borrow().clone()) - .unwrap_or_else(|| { - state - .shell - .outputs() - .next() - .cloned() - .expect("Backend has no outputs?") - }) -} + fn active_output(&self) -> Output { + self.user_data() + .get::() + .map(|x| x.0.borrow().clone()) + .unwrap() + } -pub fn set_active_output(seat: &Seat, output: &Output) { - if !seat - .user_data() - .insert_if_missing(|| ActiveOutput(RefCell::new(output.clone()))) - { - *seat + fn set_active_output(&self, output: &Output) { + *self .user_data() .get::() .unwrap() diff --git a/src/wayland/handlers/compositor.rs b/src/wayland/handlers/compositor.rs index 3e5c766c..c9935665 100644 --- a/src/wayland/handlers/compositor.rs +++ b/src/wayland/handlers/compositor.rs @@ -121,7 +121,7 @@ impl CompositorHandler for State { state.wl_buffer().is_some() }) { - let output = active_output(&seat, &self.common); + let output = seat.active_output(); Shell::map_window(self, &window, &output); } else { return; @@ -147,6 +147,7 @@ impl CompositorHandler for State { self.xdg_popup_ensure_initial_configure(&popup); } + /* // at last handle some special cases, like grabs and changing layer surfaces // If we would re-position the window inside the grab we would get a weird jittery animation. @@ -182,6 +183,7 @@ impl CompositorHandler for State { } } } + */ // We need to know every potential output for importing to the right gpu and scheduling a render, // so call this only after every potential surface map operation has been done. @@ -189,8 +191,8 @@ impl CompositorHandler for State { // and refresh smithays internal state self.common.shell.popups.commit(surface); - for workspace in &self.common.shell.spaces { - workspace.space.commit(surface); + for workspace in self.common.shell.workspaces.spaces_mut() { + workspace.commit(surface); } // re-arrange layer-surfaces (commits may change size and positioning) @@ -199,8 +201,7 @@ impl CompositorHandler for State { map.layer_for_surface(surface, WindowSurfaceType::ALL) .is_some() }) { - let dh = &self.common.display_handle; - layer_map_for_output(output).arrange(dh); + layer_map_for_output(output).arrange(); } // schedule a new render diff --git a/src/wayland/handlers/export_dmabuf.rs b/src/wayland/handlers/export_dmabuf.rs index 5a492a11..81b3b77e 100644 --- a/src/wayland/handlers/export_dmabuf.rs +++ b/src/wayland/handlers/export_dmabuf.rs @@ -213,7 +213,7 @@ impl ExportDmabufHandler for State { if cursor_status != CursorImageStatus::Hidden { let workspace = workspace.as_deref()?; let loc = seat.get_pointer().map(|ptr| ptr.current_location())?; - let output = active_output(seat, &self.common); + let output = seat.active_output(); if self.common.shell.active_space(&output).idx == workspace.idx { let relative = self diff --git a/src/wayland/handlers/layer_shell.rs b/src/wayland/handlers/layer_shell.rs index b5c73350..cc0d2c90 100644 --- a/src/wayland/handlers/layer_shell.rs +++ b/src/wayland/handlers/layer_shell.rs @@ -27,11 +27,11 @@ impl WlrLayerShellHandler for State { namespace: String, ) { super::mark_dirty_on_drop(&self.common, surface.wl_surface()); - let seat = self.common.last_active_seat.clone(); + let seat = self.common.last_active_seat().clone(); let output = wl_output .as_ref() .and_then(Output::from_resource) - .unwrap_or_else(|| active_output(&seat, &self.common)); + .unwrap_or_else(|| seat.active_output()); self.common.shell.pending_layers.push(( LayerSurface::new(surface, namespace), output, diff --git a/src/wayland/handlers/mod.rs b/src/wayland/handlers/mod.rs index 06590066..98ade3f1 100644 --- a/src/wayland/handlers/mod.rs +++ b/src/wayland/handlers/mod.rs @@ -4,7 +4,7 @@ pub mod buffer; pub mod compositor; pub mod data_device; pub mod dmabuf; -pub mod export_dmabuf; +//pub mod export_dmabuf; pub mod keyboard_shortcuts_inhibit; pub mod layer_shell; pub mod output; diff --git a/src/wayland/handlers/seat.rs b/src/wayland/handlers/seat.rs index 47e87444..1af64bb7 100644 --- a/src/wayland/handlers/seat.rs +++ b/src/wayland/handlers/seat.rs @@ -1,17 +1,23 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::state::State; +use crate::{ + shell::focus::target::{KeyboardFocusTarget, PointerFocusTarget}, + state::State, +}; use smithay::{ delegate_seat, input::{pointer::CursorImageStatus, SeatHandler, SeatState}, - reexports::wayland_server::{protocol::wl_surface::WlSurface, Resource}, - wayland::{data_device::set_data_device_focus, primary_selection::set_primary_focus}, + reexports::wayland_server::Resource, + wayland::{ + data_device::set_data_device_focus, primary_selection::set_primary_focus, + seat::WaylandFocus, + }, }; use std::cell::RefCell; impl SeatHandler for State { - type KeyboardFocus = WlSurface; - type PointerFocus = WlSurface; + type KeyboardFocus = KeyboardFocusTarget; + type PointerFocus = PointerFocusTarget; fn seat_state(&mut self) -> &mut SeatState { &mut self.common.seat_state @@ -35,10 +41,12 @@ impl SeatHandler for State { focused: Option<&Self::KeyboardFocus>, ) { let dh = &self.common.display_handle; - if let Some(client) = focused.and_then(|s| dh.get_client(s.id()).ok()) { - set_data_device_focus(dh, seat, Some(client)); - let client2 = focused.and_then(|s| dh.get_client(s.id()).ok()).unwrap(); - set_primary_focus(dh, seat, Some(client2)) + if let Some(client) = focused + .and_then(|t| t.wl_surface()) + .and_then(|s| dh.get_client(s.id()).ok()) + { + set_data_device_focus(dh, seat, Some(client.clone())); + set_primary_focus(dh, seat, Some(client)) } } } diff --git a/src/wayland/handlers/toplevel_management.rs b/src/wayland/handlers/toplevel_management.rs index ae6bcbe2..68a02331 100644 --- a/src/wayland/handlers/toplevel_management.rs +++ b/src/wayland/handlers/toplevel_management.rs @@ -19,18 +19,35 @@ impl ToplevelManagementHandler for State { } fn activate(&mut self, _dh: &DisplayHandle, window: &Window, seat: Option>) { - if let Some(idx) = self + for output in self .common .shell - .space_for_window(window.toplevel().wl_surface()) - .map(|w| w.idx) + .outputs() + .cloned() + .collect::>() + .iter() { - let seat = seat.unwrap_or(self.common.last_active_seat.clone()); - let output = active_output(&seat, &self.common); - if self.common.shell.active_space(&output).idx != idx { - self.common.shell.activate(&seat, &output, idx as usize); + let maybe = self + .common + .shell + .workspaces + .spaces_for_output(output) + .enumerate() + .find(|(_, w)| w.windows().any(|w| &w == window)); + if let Some((idx, workspace)) = maybe { + let seat = seat.unwrap_or(self.common.last_active_seat().clone()); + let mapped = workspace + .mapped() + .find(|m| m.windows().any(|(w, _)| &w == window)) + .unwrap() + .clone(); + + std::mem::drop(workspace); + self.common.shell.activate(&output, idx as usize); + mapped.focus_window(window); + Common::set_focus(self, Some(&mapped.clone().into()), &seat, None); + return; } - Common::set_focus(self, Some(window.toplevel().wl_surface()), &seat, None); } } diff --git a/src/wayland/handlers/workspace.rs b/src/wayland/handlers/workspace.rs index 41344e1b..26f7aca3 100644 --- a/src/wayland/handlers/workspace.rs +++ b/src/wayland/handlers/workspace.rs @@ -29,16 +29,15 @@ impl WorkspaceHandler for State { for request in requests.into_iter() { match request { Request::Activate(handle) => { - if let Some(idx) = self + let output = self.common.last_active_seat().active_output(); + let maybe_idx = self .common .shell - .spaces - .iter() - .position(|w| w.handle == handle) - { - let seat = &self.common.last_active_seat; - let output = active_output(seat, &self.common); - self.common.shell.activate(seat, &output, idx); + .workspaces + .spaces_for_output(&output) + .position(|w| w.handle == handle); + if let Some(idx) = maybe_idx { + self.common.shell.activate(&output, idx); } } _ => {} diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index a43edc8f..e049710b 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -5,7 +5,7 @@ use smithay::{ delegate_xdg_shell, desktop::{ find_popup_root_surface, Kind, PopupGrab, PopupKeyboardGrab, PopupKind, PopupPointerGrab, - PopupUngrabStrategy, Window, WindowSurfaceType, + PopupUngrabStrategy, Window, }, input::{ pointer::{Focus, GrabStartData as PointerGrabStartData}, @@ -17,8 +17,12 @@ use smithay::{ wayland_server::protocol::{wl_output::WlOutput, wl_seat::WlSeat, wl_surface::WlSurface}, }, utils::Serial, - wayland::shell::xdg::{ - Configure, PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState, + wayland::{ + seat::WaylandFocus, + shell::xdg::{ + Configure, PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, + XdgShellState, + }, }, }; use std::cell::Cell; @@ -35,13 +39,10 @@ impl XdgShellHandler for State { fn new_toplevel(&mut self, surface: ToplevelSurface) { super::mark_dirty_on_drop(&self.common, surface.wl_surface()); - let seat = &self.common.last_active_seat; + let seat = self.common.last_active_seat().clone(); let window = Window::new(Kind::Xdg(surface)); self.common.shell.toplevel_info_state.new_toplevel(&window); - self.common - .shell - .pending_windows - .push((window, seat.clone())); + self.common.shell.pending_windows.push((window, seat)); // We will position the window after the first commit, when we know its size hints } @@ -72,17 +73,9 @@ impl XdgShellHandler for State { // If we would re-position the window inside the grab we would get a weird jittery animation. // We only want to resize once the client has acknoledged & commited the new size, // so we need to carefully track the state through different handlers. - if let Some(window) = - self.common - .shell - .space_for_window(&surface) - .and_then(|workspace| { - workspace - .space - .window_for_surface(&surface, WindowSurfaceType::TOPLEVEL) - }) - { - crate::shell::layout::floating::ResizeSurfaceGrab::ack_configure(window, configure) + if let Some(mapped) = self.common.shell.element_for_surface(&surface) { + //TODO + //crate::shell::layout::floating::ResizeSurfaceGrab::ack_configure(window, configure) } } } @@ -90,12 +83,16 @@ impl XdgShellHandler for State { fn grab(&mut self, surface: PopupSurface, seat: WlSeat, serial: Serial) { let seat = Seat::from_resource(&seat).unwrap(); let kind = PopupKind::Xdg(surface); - if let Ok(root) = find_popup_root_surface(&kind) { + if let Some(root) = find_popup_root_surface(&kind) + .ok() + .and_then(|root| self.common.shell.element_for_surface(&root)) + { + let target = root.clone().into(); let ret = self .common .shell .popups - .grab_popup(root, kind, &seat, serial); + .grab_popup(target, kind, &seat, serial); if let Ok(mut grab) = ret { if let Some(keyboard) = seat.get_keyboard() { @@ -157,18 +154,9 @@ impl XdgShellHandler for State { fn move_request(&mut self, surface: ToplevelSurface, seat: WlSeat, serial: Serial) { let seat = Seat::from_resource(&seat).unwrap(); if let Some(start_data) = check_grab_preconditions(&seat, surface.wl_surface(), serial) { - let workspace = self - .common - .shell - .space_for_window_mut(surface.wl_surface()) - .unwrap(); - let window = workspace - .space - .window_for_surface(surface.wl_surface(), WindowSurfaceType::TOPLEVEL) - .unwrap() - .clone(); - - Shell::move_request(self, &window, &seat, serial, start_data); + if let Some(mapped) = self.common.shell.element_for_surface(surface.wl_surface()) { + // Shell::move_request(self, &window, &seat, serial, start_data); + } } } @@ -181,35 +169,31 @@ impl XdgShellHandler for State { ) { let seat = Seat::from_resource(&seat).unwrap(); if let Some(start_data) = check_grab_preconditions(&seat, surface.wl_surface(), serial) { - Workspace::resize_request(self, surface.wl_surface(), &seat, serial, start_data, edges); + if let Some(mapped) = self.common.shell.element_for_surface(surface.wl_surface()) { + // Shell::resize_request(self, mapped, &seat, serial, start_data, edges); + } } } fn maximize_request(&mut self, surface: ToplevelSurface) { let surface = surface.wl_surface(); - let seat = &self.common.last_active_seat; - let output = active_output(seat, &self.common); + let seat = self.common.last_active_seat(); + let output = seat.active_output(); - if let Some(workspace) = self.common.shell.space_for_window_mut(surface) { - let window = workspace - .space - .window_for_surface(surface, WindowSurfaceType::TOPLEVEL) - .unwrap() - .clone(); - workspace.maximize_request(&window, &output) + if let Some(mapped) = self.common.shell.element_for_surface(surface).cloned() { + if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { + //workspace.maximize_request(mapped, &output) + } } } fn unmaximize_request(&mut self, surface: ToplevelSurface) { let surface = surface.wl_surface(); - if let Some(workspace) = self.common.shell.space_for_window_mut(surface) { - let window = workspace - .space - .window_for_surface(surface, WindowSurfaceType::TOPLEVEL) - .unwrap() - .clone(); - workspace.unmaximize_request(&window) + if let Some(mapped) = self.common.shell.element_for_surface(surface).cloned() { + if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { + //workspace.unmaximize_request(mapped, &output) + } } } @@ -218,29 +202,25 @@ impl XdgShellHandler for State { .as_ref() .and_then(Output::from_resource) .unwrap_or_else(|| { - let seat = &self.common.last_active_seat; - active_output(seat, &self.common) + let seat = self.common.last_active_seat(); + seat.active_output() }); let surface = surface.wl_surface(); - if let Some(workspace) = self.common.shell.space_for_window_mut(surface) { - let window = workspace - .space - .window_for_surface(surface, WindowSurfaceType::TOPLEVEL) - .unwrap() - .clone(); - workspace.fullscreen_request(&window, &output) + if let Some(mapped) = self.common.shell.element_for_surface(surface).cloned() { + if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { + workspace.fullscreen_request(&mapped.active_window(), &output) + } } } fn unfullscreen_request(&mut self, surface: ToplevelSurface) { - let surface = surface.wl_surface(); - if let Some(workspace) = self.common.shell.space_for_window_mut(surface) { - let window = workspace - .space - .window_for_surface(surface, WindowSurfaceType::TOPLEVEL) - .unwrap() - .clone(); + if let Some((workspace, window)) = self.common.shell.workspaces.spaces_mut().find_map(|w| { + let window = w + .windows() + .find(|w| w.toplevel().wl_surface() == surface.wl_surface()); + window.map(|win| (w, win)) + }) { workspace.unfullscreen_request(&window) } } @@ -270,7 +250,6 @@ fn check_grab_preconditions( .as_ref() .unwrap() .0 - .id() .same_client_as(&surface.id()) { return None; diff --git a/src/wayland/handlers/xdg_shell/popup.rs b/src/wayland/handlers/xdg_shell/popup.rs index ef0aed53..1e8b067e 100644 --- a/src/wayland/handlers/xdg_shell/popup.rs +++ b/src/wayland/handlers/xdg_shell/popup.rs @@ -3,8 +3,7 @@ use crate::{shell::Shell, utils::prelude::*}; use smithay::{ desktop::{ - layer_map_for_output, LayerSurface, PopupKind, PopupManager, Space, Window, - WindowSurfaceType, + layer_map_for_output, LayerSurface, PopupKind, PopupManager, Window, WindowSurfaceType, }, output::Output, reexports::{ @@ -27,12 +26,19 @@ use std::sync::Mutex; impl Shell { pub fn unconstrain_popup(&self, surface: &PopupSurface, positioner: &PositionerState) { if let Some(parent) = get_popup_toplevel(&surface) { - if let Some(workspace) = self.space_for_window(&parent) { - let window = workspace - .space - .window_for_surface(&parent, WindowSurfaceType::ALL) + if let Some(elem) = self.element_for_surface(&parent) { + let workspace = self.space_for(elem).unwrap(); + let element_loc = workspace.element_geometry(elem).unwrap().loc; + let (window, offset) = elem + .windows() + .find(|(w, _)| w.toplevel().wl_surface() == &parent) .unwrap(); - unconstrain_xdg_popup(surface, positioner, &workspace.space, window); + let window_geo_offset = window.geometry().loc; + let window_loc = element_loc + offset + window_geo_offset; + let anchor_point = get_anchor_point(&positioner) + window_loc; + if let Some(output) = workspace.output_under(anchor_point) { + unconstrain_xdg_popup(surface, positioner, window_loc, output.geometry()); + } } else if let Some((output, layer_surface)) = self.outputs().find_map(|o| { let map = layer_map_for_output(o); map.layer_for_surface(&parent, WindowSurfaceType::ALL) @@ -42,15 +48,14 @@ impl Shell { } } } - - pub fn update_reactive_popups(&self, window: &Window) { - if let Some(workspace) = self.space_for_window(window.toplevel().wl_surface()) { - update_reactive_popups(&workspace.space, window); - } - } } -pub fn update_reactive_popups(space: &Space, window: &Window) { +pub fn update_reactive_popups<'a>( + window: &Window, + loc: Point, + outputs: impl Iterator, +) { + let output_geo = outputs.map(|o| o.geometry()).collect::>(); for (popup, _) in PopupManager::popups_for_surface(window.toplevel().wl_surface()) { match popup { PopupKind::Xdg(surface) => { @@ -64,12 +69,19 @@ pub fn update_reactive_popups(space: &Space, window: &Window) { attributes.current.positioner.clone() }); if positioner.reactive { - unconstrain_xdg_popup(&surface, &positioner, space, window); - if let Err(err) = surface.send_configure() { - slog_scope::warn!( - "Compositor bug: Unable to re-configure reactive popup: {}", - err - ); + let anchor_point = get_anchor_point(&positioner) + loc; + if let Some(rect) = output_geo + .iter() + .find(|geo| geo.contains(anchor_point)) + .copied() + { + unconstrain_xdg_popup(&surface, &positioner, loc, rect); + if let Err(err) = surface.send_configure() { + slog_scope::warn!( + "Compositor bug: Unable to re-configure reactive popup: {}", + err + ); + } } } } @@ -80,32 +92,18 @@ pub fn update_reactive_popups(space: &Space, window: &Window) { fn unconstrain_xdg_popup( surface: &PopupSurface, positioner: &PositionerState, - space: &Space, - window: &Window, + window_loc: Point, + rect: Rectangle, ) { - let anchor_point = get_anchor_point(&positioner) + space.window_location(&window).unwrap(); - if let Some(output_rect) = space - .outputs_for_window(window) - .into_iter() - .find(|o| { - space - .output_geometry(o) - .map(|rect| rect.contains(anchor_point)) - .unwrap_or(false) - }) - .map(|o| space.output_geometry(&o).unwrap()) - { - // the output_rect represented relative to the parents coordinate system - let mut relative = output_rect; - relative.loc -= space.window_location(&window).unwrap(); - let offset = check_constrained(&surface, positioner.get_geometry(), relative); + let mut relative = rect; + relative.loc -= window_loc; + let offset = check_constrained(&surface, positioner.get_geometry(), relative); - if offset.x != 0 || offset.y != 0 { - slog_scope::debug!("Unconstraining popup: {:?}", surface); - if !unconstrain_flip(&surface, &positioner, relative) { - if !unconstrain_slide(&surface, &positioner, relative) { - unconstrain_resize(&surface, &positioner, relative); - } + if offset.x != 0 || offset.y != 0 { + slog_scope::debug!("Unconstraining popup: {:?}", surface); + if !unconstrain_flip(&surface, &positioner, relative) { + if !unconstrain_slide(&surface, &positioner, relative) { + unconstrain_resize(&surface, &positioner, relative); } } } diff --git a/src/wayland/protocols/mod.rs b/src/wayland/protocols/mod.rs index 2129bd98..069f16c5 100644 --- a/src/wayland/protocols/mod.rs +++ b/src/wayland/protocols/mod.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only pub mod drm; -pub mod export_dmabuf; +//pub mod export_dmabuf; pub mod output_configuration; pub mod toplevel_info; pub mod toplevel_management; diff --git a/src/wayland/protocols/output_configuration.rs b/src/wayland/protocols/output_configuration.rs index 45cb341d..9a98c52d 100644 --- a/src/wayland/protocols/output_configuration.rs +++ b/src/wayland/protocols/output_configuration.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only use smithay::{ - output::{Mode, Output, OutputData}, + output::{Mode, Output}, reexports::{ wayland_protocols_wlr::output_management::v1::server::{ zwlr_output_configuration_head_v1::{self, ZwlrOutputConfigurationHeadV1}, @@ -17,6 +17,7 @@ use smithay::{ }, }, utils::{Logical, Physical, Point, Size, Transform}, + wayland::output::WlOutputData, }; use std::{ convert::{TryFrom, TryInto}, @@ -498,7 +499,7 @@ where impl OutputConfigurationState where D: GlobalDispatch - + GlobalDispatch + + GlobalDispatch + Dispatch + Dispatch + Dispatch diff --git a/src/wayland/protocols/toplevel_info.rs b/src/wayland/protocols/toplevel_info.rs index 6a4c9b25..b9d154a3 100644 --- a/src/wayland/protocols/toplevel_info.rs +++ b/src/wayland/protocols/toplevel_info.rs @@ -413,7 +413,7 @@ fn send_toplevel_to_client( .iter() .filter(|o| !handle_state.outputs.contains(o)) { - new_output.with_client_outputs(dh, &client, |_dh, wl_output| { + new_output.with_client_outputs(&client, |wl_output| { instance.output_enter(wl_output); }); changed = true; @@ -423,7 +423,7 @@ fn send_toplevel_to_client( .iter() .filter(|o| !state.outputs.contains(o)) { - old_output.with_client_outputs(dh, &client, |_dh, wl_output| { + old_output.with_client_outputs(&client, |wl_output| { instance.output_leave(wl_output); }); changed = true; diff --git a/src/wayland/protocols/workspace.rs b/src/wayland/protocols/workspace.rs index 07cfdff2..7915b805 100644 --- a/src/wayland/protocols/workspace.rs +++ b/src/wayland/protocols/workspace.rs @@ -821,7 +821,7 @@ where .iter() .filter(|o| !handle_state.outputs.contains(o)) { - new_output.with_client_outputs(dh, &client, |_dh, wl_output| { + new_output.with_client_outputs(&client, |wl_output| { instance.output_enter(wl_output); }); changed = true; @@ -831,7 +831,7 @@ where .iter() .filter(|o| !group.outputs.contains(o)) { - old_output.with_client_outputs(dh, &client, |_dh, wl_output| { + old_output.with_client_outputs(&client, |wl_output| { instance.output_leave(wl_output); }); changed = true;