diff --git a/src/config/mod.rs b/src/config/mod.rs index ea603e3e..215bd1e8 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - shell::{layout::FocusDirection, Shell}, - state::{BackendData, State}, + shell::{focus::FocusDirection, Shell}, + state::{BackendData, Data}, }; use serde::{Deserialize, Serialize}; pub use smithay::{ @@ -33,7 +33,13 @@ pub struct Config { #[derive(Debug, Deserialize)] pub struct StaticConfig { pub key_bindings: HashMap, - pub workspace_mode: crate::shell::Mode, + pub workspace_mode: WorkspaceMode, +} + +#[derive(Debug, Deserialize, Clone, Copy)] +pub enum WorkspaceMode { + OutputBound, + Global, } pub struct DynamicConfig { @@ -209,7 +215,7 @@ impl Config { StaticConfig { key_bindings: HashMap::new(), - workspace_mode: crate::shell::Mode::global(), + workspace_mode: WorkspaceMode::Global, } } @@ -273,7 +279,7 @@ impl Config { outputs: impl Iterator>, backend: &mut BackendData, shell: &mut Shell, - loop_handle: &LoopHandle<'_, State>, + loop_handle: &LoopHandle<'_, Data>, ) { let outputs = outputs.map(|x| x.borrow().clone()).collect::>(); let mut infos = outputs diff --git a/src/input/mod.rs b/src/input/mod.rs index 3cae20db..1a6ad7c1 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -3,17 +3,17 @@ use crate::{ config::Action, shell::Workspace, - state::{Common, State}, + utils::prelude::*, }; use smithay::{ backend::input::{Device, DeviceCapability, InputBackend, InputEvent, KeyState}, - desktop::{layer_map_for_output, Kind, Space, WindowSurfaceType}, - reexports::wayland_server::{protocol::wl_surface::WlSurface, Display}, + desktop::{layer_map_for_output, Kind, WindowSurfaceType}, + reexports::wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle, Resource}, utils::{Logical, Point, Rectangle}, wayland::{ data_device::set_data_device_focus, output::Output, - seat::{keysyms, CursorImageStatus, FilterResult, KeysymHandle, Seat, XkbConfig}, + seat::{keysyms, CursorImageStatus, FilterResult, KeysymHandle, Seat, XkbConfig, MotionEvent, ButtonEvent}, shell::wlr_layer::Layer as WlrLayer, SERIAL_COUNTER, }, @@ -82,8 +82,8 @@ impl Devices { } } -pub fn add_seat(display: &mut Display, name: String) -> Seat { - let (seat, _) = Seat::new(display, name, None); +pub fn add_seat(dh: &DisplayHandle, name: String) -> Seat { + let seat = Seat::::new(dh, name, None); let userdata = seat.user_data(); userdata.insert_if_missing(|| Devices::new()); userdata.insert_if_missing(|| SupressedKeys::new()); @@ -91,36 +91,8 @@ pub fn add_seat(display: &mut Display, name: String) -> Seat { seat } -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?") - }) -} - -pub fn set_active_output(seat: &Seat, output: &Output) { - if !seat - .user_data() - .insert_if_missing(|| ActiveOutput(RefCell::new(output.clone()))) - { - *seat - .user_data() - .get::() - .unwrap() - .0 - .borrow_mut() = output.clone(); - } -} - impl State { - pub fn process_input_event(&mut self, event: InputEvent) { + pub fn process_input_event(&mut self, dh: &DisplayHandle, event: InputEvent) { use smithay::backend::input::Event; match event { @@ -131,12 +103,16 @@ impl State { for cap in devices.add_device(&device) { match cap { DeviceCapability::Keyboard => { + let dh_clone = dh.clone(); let _ = - seat.add_keyboard(XkbConfig::default(), 200, 25, |seat, focus| { - set_data_device_focus( - seat, - focus.and_then(|s| s.as_ref().client()), - ) + seat.add_keyboard(XkbConfig::default(), 200, 25, move |seat, focus| { + if let Some(client) = focus.and_then(|s| dh_clone.get_client(s.id()).ok()) { + set_data_device_focus( + &dh_clone, + seat, + Some(client), + ) + } }); } DeviceCapability::Pointer => { @@ -209,7 +185,7 @@ impl State { if let Some(action) = seat .get_keyboard() .unwrap() - .input(keycode, state, serial, time, |modifiers, handle| { + .input(dh, keycode, state, serial, time, |modifiers, handle| { if state == KeyState::Released && userdata.get::().unwrap().filter(&handle) { @@ -307,6 +283,7 @@ impl State { x => x - 1, }; self.common.shell.activate( + dh, seat, ¤t_output, workspace as usize, @@ -326,12 +303,16 @@ impl State { } Action::Focus(focus) => { let current_output = active_output(seat, &self.common); - self.common.shell.move_focus( - seat, - ¤t_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( *focus, - self.common.seats.iter(), - ); + seat, + &mut workspace.space, + focus_stack.iter() + ) { + self.common.set_focus(dh, Some(window.toplevel().wl_surface()), seat, None); + } } Action::Fullscreen => { let current_output = active_output(seat, &self.common); @@ -344,9 +325,11 @@ impl State { } Action::Orientation(orientation) => { let output = active_output(seat, &self.common); - self.common + let workspace = self.common .shell - .set_orientation(&seat, &output, *orientation); + .active_space_mut(&output); + let focus_stack = workspace.focus_stack(seat); + workspace.tiling_layer.update_orientation(*orientation, &seat, &mut workspace.space, focus_stack.iter()); } Action::Spawn(command) => { if let Err(err) = std::process::Command::new("/bin/sh") @@ -382,9 +365,7 @@ impl State { .shell .outputs() .find(|output| { - self.common - .shell - .output_geometry(output) + output.geometry() .to_f64() .contains(position) }) @@ -393,7 +374,7 @@ impl State { if output != current_output { set_active_output(seat, &output); } - let output_geometry = self.common.shell.output_geometry(&output); + let output_geometry = output.geometry(); position.x = 0.0f64 .max(position.x) @@ -415,13 +396,14 @@ impl State { output_geometry, &workspace, ); - handle_window_movement( - under.as_ref().map(|(s, _)| s), - &mut workspace.space, - ); seat.get_pointer() .unwrap() - .motion(position, under, serial, event.time()); + .motion(self, dh, &MotionEvent { + location: position, + focus: under, + serial, + time: event.time() + }); #[cfg(feature = "debug")] if self.common.seats.iter().position(|x| x == seat).unwrap() == 0 { @@ -447,7 +429,7 @@ impl State { let devices = userdata.get::().unwrap(); if devices.has_device(&device) { let output = active_output(seat, &self.common); - let geometry = self.common.shell.output_geometry(&output); + let geometry = output.geometry(); let position = geometry.loc.to_f64() + event.position_transformed(geometry.size); let relative_pos = self @@ -463,13 +445,14 @@ impl State { geometry, &workspace, ); - handle_window_movement( - under.as_ref().map(|(s, _)| s), - &mut workspace.space, - ); seat.get_pointer() .unwrap() - .motion(position, under, serial, event.time()); + .motion(self, dh, &MotionEvent { + location: position, + focus: under, + serial, + time: event.time() + }); #[cfg(feature = "debug")] if self.common.seats.iter().position(|x| x == seat).unwrap() == 0 { @@ -536,7 +519,7 @@ impl State { { let output = active_output(seat, &self.common); let pos = seat.get_pointer().unwrap().current_location(); - let output_geo = self.common.shell.output_geometry(&output); + let output_geo = output.geometry(); let relative_pos = self .common .shell @@ -558,7 +541,7 @@ impl State { - layer_loc.to_f64(), WindowSurfaceType::ALL, ) - .and_then(|(_, _)| layer.get_surface().cloned()); + .map(|(_, _)| layer.wl_surface().clone()); } } else { under = window @@ -566,7 +549,7 @@ impl State { pos - output_geo.loc.to_f64(), WindowSurfaceType::ALL, ) - .and_then(|(_, _)| window.toplevel().get_surface().cloned()); + .map(|(_, _)| window.toplevel().wl_surface().clone()); } } else { if let Some(layer) = layers @@ -584,7 +567,7 @@ impl State { - layer_loc.to_f64(), WindowSurfaceType::ALL, ) - .and_then(|(_, _)| layer.get_surface().cloned()); + .map(|(_, _)| layer.wl_surface().clone()); } } else if let Some((window, _, _)) = workspace.space.surface_under( @@ -592,7 +575,7 @@ impl State { WindowSurfaceType::ALL, ) { - under = window.toplevel().get_surface().cloned(); + under = Some(window.toplevel().wl_surface().clone()); } else if let Some(layer) = layers.layer_under(WlrLayer::Bottom, pos).or_else( || layers.layer_under(WlrLayer::Background, pos), @@ -607,12 +590,12 @@ impl State { - layer_loc.to_f64(), WindowSurfaceType::ALL, ) - .and_then(|(_, _)| layer.get_surface().cloned()); + .map(|(_, _)| layer.wl_surface().clone()); } }; } - self.common.set_focus(under.as_ref(), seat, Some(serial)); + self.common.set_focus(dh, under.as_ref(), seat, Some(serial)); } wl_pointer::ButtonState::Pressed } @@ -620,7 +603,12 @@ impl State { }; seat.get_pointer() .unwrap() - .button(button, state, serial, event.time()); + .button(self, dh, &ButtonEvent { + button, + state, + serial, + time: event.time() + }); break; } } @@ -712,7 +700,7 @@ impl State { } else if source == wl_pointer::AxisSource::Finger { frame = frame.stop(wl_pointer::Axis::VerticalScroll); } - seat.get_pointer().unwrap().axis(frame); + seat.get_pointer().unwrap().axis(self, dh, frame); } break; } @@ -781,16 +769,3 @@ impl State { } } } - -pub fn handle_window_movement(surface: Option<&WlSurface>, space: &mut Space) { - // TODO: this is why to hardcoded and hacky, but wayland-rs 0.30 will make this unnecessary anyway. - if let Some(surface) = surface { - if let Some(window) = space.window_for_surface(&surface, WindowSurfaceType::TOPLEVEL).cloned() { - if let Some(new_position) = - crate::shell::layout::floating::MoveSurfaceGrab::apply_move_state(&window) - { - space.map_window(&window, new_position, true); - } - } - } -} diff --git a/src/shell/focus.rs b/src/shell/focus.rs new file mode 100644 index 00000000..bd3c6d01 --- /dev/null +++ b/src/shell/focus.rs @@ -0,0 +1,270 @@ +use std::{ + cell::{Ref, RefMut, RefCell}, + collections::HashMap, + sync::Mutex, +}; +use indexmap::IndexSet; +use smithay::{ + desktop::{ + Window, WindowSurfaceType, + PopupUngrabStrategy, + }, + reexports::wayland_server::{ + DisplayHandle, + protocol::wl_surface::WlSurface, + }, + wayland::{ + compositor::with_states, + seat::Seat, + shell::xdg::XdgToplevelSurfaceRoleAttributes, + Serial, + SERIAL_COUNTER, + }, + utils::IsAlive, +}; +use crate::{ + state::Common, + shell::{Shell, OutputBoundState, Workspace, WorkspaceMode}, + wayland::handlers::xdg_shell::PopupGrabData, + utils::prelude::*, +}; + +#[derive(Debug, serde::Deserialize, Clone, Copy, PartialEq, Eq)] +pub enum FocusDirection { + Left, + Right, + Up, + Down, + In, + Out, +} + +pub struct FocusStack<'a>(Ref<'a, IndexSet>); +pub struct FocusStackMut<'a>(RefMut<'a, IndexSet>); + +impl<'a> FocusStack<'a> { + pub fn last(&self) -> Option { + self.0.iter().rev().find(|w| w.toplevel().alive()).cloned() + } + + pub fn iter(&self) -> impl Iterator { + self.0.iter().rev().filter(|w| w.toplevel().alive()) + } +} + +impl<'a> FocusStackMut<'a> { + pub fn append(&mut self, window: &Window) { + self.0.retain(|w| w.toplevel().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 iter(&self) -> impl Iterator { + self.0.iter().rev().filter(|w| w.toplevel().alive()) + } +} + +type FocusStackData = RefCell<(HashMap>, IndexSet)>; + +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>); + +impl ActiveFocus { + fn set(seat: &Seat, surface: Option) { + if !seat + .user_data() + .insert_if_missing(|| ActiveFocus(RefCell::new(surface.clone()))) + { + *seat + .user_data() + .get::() + .unwrap() + .0 + .borrow_mut() = surface; + } + } + + fn get(seat: &Seat) -> Option { + seat.user_data() + .get::() + .and_then(|a| a.0.borrow().clone()) + } +} + +impl Shell { + pub fn set_focus<'a>( + &mut self, + dh: &DisplayHandle, + surface: Option<&WlSurface>, + 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) = self.space_for_surface_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(dh, PopupUngrabStrategy::All); + } + } + } + } + } + } + + // update keyboard focus + if let Some(keyboard) = active_seat.get_keyboard() { + ActiveFocus::set(active_seat, surface.cloned()); + keyboard.set_focus( + dh, + surface, + serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()), + ); + } + } + + fn update_active<'a>(&mut self, seats: impl Iterator>) { + // 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()) + }) + .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], + }; + for focused in focused_windows.iter() { + workspace.space.raise_window(focused, true); + } + for window in workspace.space.windows() { + window.set_activated(focused_windows.contains(window)); + window.configure(); + } + } + } +} + +impl Common { + pub fn set_focus( + &mut self, + dh: &DisplayHandle, + surface: Option<&WlSurface>, + active_seat: &Seat, + serial: Option, + ) { + self.shell.set_focus(dh, surface, active_seat, serial); + self.shell.update_active(self.seats.iter()); + } + + pub fn refresh_focus(&mut self, dh: &DisplayHandle) { + for seat in &self.seats { + let mut fixup = false; + let output = active_output(seat, &self); + let last_known_focus = ActiveFocus::get(seat); + + if let Some(surface) = last_known_focus { + if surface.alive() { + let is_toplevel = with_states(&surface, |states| { + states + .data_map + .get::>() + .is_some() + }); + if !is_toplevel { + continue; + } + + let workspace = self.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) { + fixup = true; + } + } else { + fixup = true; + } + } else { + fixup = true; + } + } + + if fixup { + // also remove popup grabs, if we are switching focus + if let Some(mut popup_grab) = seat + .user_data() + .get::() + .and_then(|x| x.take()) + { + if !popup_grab.has_ended() { + popup_grab.ungrab(dh, PopupUngrabStrategy::All); + } + } + + // update keyboard focus + let surface = self + .shell + .active_space(&output) + .focus_stack(seat) + .last() + .map(|w| w.toplevel().wl_surface().clone()); + if let Some(keyboard) = seat.get_keyboard() { + keyboard.set_focus(dh, surface.as_ref(), SERIAL_COUNTER.next_serial()); + ActiveFocus::set(seat, surface); + } + } + } + + self.shell.update_active(self.seats.iter()) + } +} diff --git a/src/shell/handler.rs b/src/shell/handler.rs deleted file mode 100644 index 9931e696..00000000 --- a/src/shell/handler.rs +++ /dev/null @@ -1,434 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only - -use crate::{ - config::Config, - input::active_output, - state::{BackendData, State}, - utils::SurfaceDropNotifier, -}; -use smithay::{ - backend::renderer::utils::on_commit_buffer_handler, - desktop::{ - layer_map_for_output, Kind, LayerSurface, PopupGrab, PopupKeyboardGrab, PopupKind, - PopupPointerGrab, PopupUngrabStrategy, Window, WindowSurfaceType, - }, - reexports::{ - wayland_protocols::xdg_shell::server::xdg_toplevel, - wayland_server::{protocol::wl_surface::WlSurface, Display}, - }, - wayland::{ - compositor::{compositor_init, with_states}, - output::Output, - seat::{PointerGrabStartData, Seat}, - shell::{ - wlr_layer::{wlr_layer_shell_init, LayerShellRequest, LayerSurfaceAttributes}, - xdg::{ - xdg_shell_init, Configure, XdgPopupSurfaceRoleAttributes, XdgRequest, - XdgToplevelSurfaceRoleAttributes, - }, - }, - Serial, - }, -}; -use std::{cell::Cell, sync::Mutex}; - -pub type PopupGrabData = Cell>; - -pub fn init_shell(config: &Config, display: &mut Display) -> super::Shell { - compositor_init( - display, - move |surface, mut ddata| { - on_commit_buffer_handler(&surface); - let state = ddata.get::().unwrap(); - state - .common - .shell - .commit(&surface, state.common.seats.iter()); - commit(&surface, state) - }, - None, - ); - - let (_xdg_shell_state, _xdg_global) = xdg_shell_init( - display, - move |event, mut ddata| { - let state = &mut ddata.get::().unwrap().common; - - match event { - XdgRequest::NewToplevel { surface } => { - state.pending_toplevels.push(surface.clone()); - - let seat = &state.last_active_seat; - let output = active_output(seat, &*state); - let space = state.shell.active_space_mut(&output); - let window = Window::new(Kind::Xdg(surface)); - space.pending_window(window, seat); - // We will position the window after the first commit, when we know its size - } - XdgRequest::NewPopup { surface, .. } => { - state - .shell - .popups - .track_popup(PopupKind::from(surface)) - .unwrap(); - } - XdgRequest::RePosition { - surface, - positioner, - token, - } => { - let result = surface.with_pending_state(|state| { - // TODO: This is a simplification, a proper compositor would - // calculate the geometry of the popup here. - // For now we just use the default implementation here that does not take the - // window position and output constraints into account. - let geometry = positioner.get_geometry(); - state.geometry = geometry; - state.positioner = positioner; - }); - - if result.is_ok() { - surface.send_repositioned(token); - } - } - XdgRequest::Move { - surface, - seat, - serial, - } => { - let seat = Seat::from_resource(&seat).unwrap(); - if let Some(start_data) = - check_grab_preconditions(&seat, surface.get_surface(), serial) - { - let workspace = state - .shell - .space_for_surface_mut(surface.get_surface().unwrap()) - .unwrap(); - let window = workspace - .space - .window_for_surface(surface.get_surface().unwrap(), WindowSurfaceType::TOPLEVEL) - .unwrap() - .clone(); - - workspace.move_request(&window, &seat, serial, start_data); - } - } - - XdgRequest::Resize { - surface, - seat, - serial, - edges, - } => { - let seat = Seat::from_resource(&seat).unwrap(); - if let Some(start_data) = - check_grab_preconditions(&seat, surface.get_surface(), serial) - { - let workspace = state - .shell - .space_for_surface_mut(surface.get_surface().unwrap()) - .unwrap(); - let window = workspace - .space - .window_for_surface(surface.get_surface().unwrap(), WindowSurfaceType::TOPLEVEL) - .unwrap() - .clone(); - - workspace.resize_request(&window, &seat, serial, start_data, edges); - } - } - XdgRequest::AckConfigure { - surface, - configure: Configure::Toplevel(configure), - } => { - // TODO: This is way to hardcoded and hacky, but wayland-rs 0.30 will make this unnecessary so don't bother. - if let Some(window) = state - .shell - .space_for_surface(&surface) - .and_then(|workspace| workspace.space.window_for_surface(&surface, WindowSurfaceType::TOPLEVEL)) - { - crate::shell::layout::floating::ResizeSurfaceGrab::ack_configure( - window, configure, - ) - } - } - XdgRequest::Maximize { surface } => { - if let Some(surface) = surface.get_surface() { - let seat = &state.last_active_seat; - let output = active_output(seat, &*state); - - if let Some(workspace) = state.shell.space_for_surface_mut(surface) { - let window = - workspace.space.window_for_surface(surface, WindowSurfaceType::TOPLEVEL).unwrap().clone(); - workspace.maximize_request(&window, &output) - } - } - } - XdgRequest::UnMaximize { surface } => { - let ret = surface.with_pending_state(|state| { - state.states.unset(xdg_toplevel::State::Maximized); - state.size = None; - }); - - if ret.is_ok() { - surface.send_configure(); - } - } - XdgRequest::Grab { - serial, - surface, - seat, - } => { - let seat = Seat::from_resource(&seat).unwrap(); - let ret = state.shell.popups.grab_popup(surface.into(), &seat, serial); - - if let Ok(mut grab) = ret { - if let Some(keyboard) = seat.get_keyboard() { - if keyboard.is_grabbed() - && !(keyboard.has_grab(serial) - || keyboard.has_grab(grab.previous_serial().unwrap_or(serial))) - { - grab.ungrab(PopupUngrabStrategy::All); - return; - } - state.set_focus(grab.current_grab().as_ref(), &seat, Some(serial)); - keyboard.set_grab(PopupKeyboardGrab::new(&grab), serial); - } - - if let Some(pointer) = seat.get_pointer() { - if pointer.is_grabbed() - && !(pointer.has_grab(serial) - || pointer.has_grab( - grab.previous_serial().unwrap_or_else(|| grab.serial()), - )) - { - grab.ungrab(PopupUngrabStrategy::All); - return; - } - pointer.set_grab(PopupPointerGrab::new(&grab), serial, 0); - } - - seat.user_data() - .insert_if_missing(|| PopupGrabData::new(None)); - seat.user_data() - .get::() - .unwrap() - .set(Some(grab)); - } - } - XdgRequest::Fullscreen { surface, output } => { - let output = output - .as_ref() - .and_then(Output::from_resource) - .unwrap_or_else(|| { - let seat = &state.last_active_seat; - active_output(seat, &*state) - }); - if let Some(surface) = surface.get_surface() { - if let Some(workspace) = state.shell.space_for_surface_mut(surface) { - let window = - workspace.space.window_for_surface(surface, WindowSurfaceType::TOPLEVEL).unwrap().clone(); - workspace.fullscreen_request(&window, &output) - } - } - } - XdgRequest::UnFullscreen { surface } => { - if let Some(surface) = surface.get_surface() { - if let Some(workspace) = state.shell.space_for_surface_mut(surface) { - let window = - workspace.space.window_for_surface(surface, WindowSurfaceType::TOPLEVEL).unwrap().clone(); - workspace.unfullscreen_request(&window) - } - } - } - _ => { /*TODO*/ } - } - }, - None, - ); - - let (_layer_shell_state, _layer_global) = wlr_layer_shell_init( - display, - |event, mut ddata| match event { - LayerShellRequest::NewLayerSurface { - surface, - output: wl_output, - namespace, - .. - } => { - let state = &mut ddata.get::().unwrap().common; - let seat = state.last_active_seat.clone(); - let output = wl_output - .as_ref() - .and_then(Output::from_resource) - .unwrap_or_else(|| active_output(&seat, &*state)); - state.shell.active_space_mut(&output).pending_layer( - LayerSurface::new(surface, namespace), - &output, - &seat, - ); - } - _ => {} - }, - None, - ); - - super::Shell::new(config, display) -} - -fn check_grab_preconditions( - seat: &Seat, - surface: Option<&WlSurface>, - serial: Serial, -) -> Option { - let surface = if let Some(surface) = surface { - surface - } else { - return None; - }; - - // TODO: touch resize. - let pointer = seat.get_pointer().unwrap(); - - // Check that this surface has a click grab. - if !pointer.has_grab(serial) { - return None; - } - - let start_data = pointer.grab_start_data().unwrap(); - - // If the focus was for a different surface, ignore the request. - if start_data.focus.is_none() - || !start_data - .focus - .as_ref() - .unwrap() - .0 - .as_ref() - .same_client_as(surface.as_ref()) - { - return None; - } - - Some(start_data) -} - -fn commit(surface: &WlSurface, state: &mut State) { - let mut import_nodes = std::collections::HashSet::new(); - for output in state.common.shell.outputs_for_surface(&surface) { - if let BackendData::Kms(ref mut kms_state) = &mut state.backend { - if let Some(target) = kms_state.target_node_for_output(&output) { - if import_nodes.insert(target) { - kms_state.try_early_import(surface, &output, target, &state.common.shell); - } - } - } - state - .backend - .schedule_render(&state.common.event_loop_handle, &output); - } - - let state = &mut state.common; - let _ = with_states(surface, |states| { - states - .data_map - .insert_if_missing(|| SurfaceDropNotifier::from(&*state)); - }); - - if let Some(toplevel) = state.pending_toplevels.iter().find(|toplevel| { - toplevel - .get_surface() - .map(|s| s == surface) - .unwrap_or(false) - }) { - // send the initial configure if relevant - let initial_configure_sent = with_states(surface, |states| { - states - .data_map - .get::>() - .unwrap() - .lock() - .unwrap() - .initial_configure_sent - }) - .unwrap(); - if !initial_configure_sent { - toplevel.send_configure(); - } - - return; - } - - // TODO: This is way to hardcoded and hacky, but wayland-rs 0.30 will make this unnecessary so don't bother. - if let Some((space, window)) = - state - .shell - .space_for_surface_mut(surface) - .and_then(|workspace| { - workspace - .space - .window_for_surface(surface, WindowSurfaceType::TOPLEVEL) - .cloned() - .map(|window| (&mut workspace.space, window)) - }) - { - let new_location = crate::shell::layout::floating::ResizeSurfaceGrab::apply_resize_state( - &window, - space.window_location(&window).unwrap(), - window.geometry().size, - ); - if let Some(location) = new_location { - space.map_window(&window, location, true); - } - - return; - } - - if let Some(popup) = state.shell.popups.find_popup(surface) { - let PopupKind::Xdg(ref popup) = popup; - let initial_configure_sent = with_states(surface, |states| { - states - .data_map - .get::>() - .unwrap() - .lock() - .unwrap() - .initial_configure_sent - }) - .unwrap(); - if !initial_configure_sent { - // NOTE: This should never fail as the initial configure is always - // allowed. - popup.send_configure().expect("initial configure failed"); - } - - return; - } - - if let Some(output) = state.shell.outputs().find(|o| { - let map = layer_map_for_output(o); - map.layer_for_surface(surface, WindowSurfaceType::TOPLEVEL).is_some() - }) { - let mut map = layer_map_for_output(output); - let layer = map.layer_for_surface(surface, WindowSurfaceType::TOPLEVEL).unwrap(); - - // send the initial configure if relevant - let initial_configure_sent = with_states(surface, |states| { - states - .data_map - .get::>() - .unwrap() - .lock() - .unwrap() - .initial_configure_sent - }) - .unwrap(); - if !initial_configure_sent { - layer.layer_surface().send_configure(); - } - - map.arrange(); - - return; - }; -} diff --git a/src/shell/layout/combined.rs b/src/shell/layout/combined.rs deleted file mode 100644 index 106b91ff..00000000 --- a/src/shell/layout/combined.rs +++ /dev/null @@ -1,151 +0,0 @@ -use super::{FocusDirection, Layout, Orientation}; -use smithay::{ - desktop::{Space, Window}, - reexports::wayland_protocols::xdg_shell::server::xdg_toplevel::ResizeEdge, - wayland::{ - seat::{PointerGrabStartData, Seat}, - Serial, - }, -}; - -struct Filtered; - -pub struct Combined { - first: A, - second: B, - windows_a: Vec, - windows_b: Vec, - filter: Box bool>, -} - -impl Combined { - pub fn new(first: A, second: B, filter: impl Fn(&Window) -> bool + 'static) -> Combined { - Combined { - first, - second, - windows_a: Vec::new(), - windows_b: Vec::new(), - filter: Box::new(filter), - } - } -} - -impl Layout for Combined { - fn map_window<'a>( - &mut self, - space: &mut Space, - window: &Window, - seat: &Seat, - focus_stack: Box + 'a>, - ) { - if (self.filter)(window) { - self.windows_b.push(window.clone()); - self.second.map_window( - space, - window, - seat, - Box::new(focus_stack.filter(|w| w.user_data().get::().is_some())), - ) - } else { - self.windows_a.push(window.clone()); - self.first.map_window( - space, - window, - seat, - Box::new(focus_stack.filter(|w| w.user_data().get::().is_none())), - ) - } - } - - fn refresh(&mut self, space: &mut Space) { - self.first.refresh(space); - self.second.refresh(space); - self.windows_a.retain(|w| w.toplevel().alive()); - self.windows_b.retain(|w| w.toplevel().alive()); - } - - fn unmap_window(&mut self, space: &mut Space, window: &Window) { - if self.windows_a.contains(window) { - self.windows_a.retain(|w| w != window); - self.first.unmap_window(space, window) - } else if self.windows_b.contains(window) { - self.windows_b.retain(|w| w != window); - self.second.unmap_window(space, window) - } - } - - fn move_request( - &mut self, - space: &mut Space, - window: &Window, - seat: &Seat, - serial: Serial, - start_data: PointerGrabStartData, - ) { - if self.windows_a.contains(window) { - self.first - .move_request(space, window, seat, serial, start_data) - } else if self.windows_b.contains(window) { - self.second - .move_request(space, window, seat, serial, start_data) - } - } - - fn resize_request( - &mut self, - space: &mut Space, - window: &Window, - seat: &Seat, - serial: Serial, - start_data: PointerGrabStartData, - edges: ResizeEdge, - ) { - if self.windows_a.contains(window) { - self.first - .resize_request(space, window, seat, serial, start_data, edges) - } else if self.windows_b.contains(window) { - self.second - .resize_request(space, window, seat, serial, start_data, edges) - } - } - - fn move_focus<'a>( - &mut self, - direction: FocusDirection, - seat: &Seat, - space: &mut Space, - focus_stack: Box + 'a>, - ) -> Option { - let focus_stack = focus_stack.collect::>(); - match self.first.move_focus( - direction, - seat, - space, - Box::new(focus_stack.clone().into_iter()), - ) { - Some(window) => Some(window), - None => { - self.second - .move_focus(direction, seat, space, Box::new(focus_stack.into_iter())) - } - } - } - - fn update_orientation<'a>( - &mut self, - orientation: Orientation, - seat: &Seat, - space: &mut Space, - focus_stack: Box + 'a>, - ) { - let focus_stack = focus_stack.collect::>(); - self.first.update_orientation( - orientation, - seat, - space, - Box::new(focus_stack.clone().into_iter()), - ); - self.second - .update_orientation(orientation, seat, space, Box::new(focus_stack.into_iter())); - } -} diff --git a/src/shell/layout/floating/grabs.rs b/src/shell/layout/floating/grabs.rs index e2a0eb35..862d4117 100644 --- a/src/shell/layout/floating/grabs.rs +++ b/src/shell/layout/floating/grabs.rs @@ -3,73 +3,70 @@ use smithay::{ desktop::{Kind, Window}, reexports::{ - wayland_protocols::xdg_shell::server::xdg_toplevel, - wayland_server::protocol::{wl_pointer::ButtonState, wl_surface}, + wayland_protocols::xdg::shell::server::xdg_toplevel, + wayland_server::DisplayHandle, }, - utils::{Logical, Point, Size}, + utils::{IsAlive, Logical, Point, Size}, wayland::{ compositor::with_states, - seat::{AxisFrame, PointerGrab, PointerGrabStartData, PointerInnerHandle}, + seat::{AxisFrame, PointerGrab, PointerGrabStartData, PointerInnerHandle, MotionEvent, ButtonEvent}, shell::xdg::{SurfaceCachedState, ToplevelConfigure, XdgToplevelSurfaceRoleAttributes}, Serial, }, }; -use std::{cell::RefCell, sync::Mutex}; - -#[derive(Debug, Default)] -struct MoveData { - new_location: Point, -} +use crate::utils::prelude::*; +use std::{ + cell::RefCell, + convert::TryFrom, + sync::Mutex, +}; pub struct MoveSurfaceGrab { start_data: PointerGrabStartData, window: Window, initial_window_location: Point, + delta: Point, } -impl PointerGrab for MoveSurfaceGrab { +impl PointerGrab for MoveSurfaceGrab { fn motion( &mut self, - handle: &mut PointerInnerHandle<'_>, - location: Point, - _focus: Option<(wl_surface::WlSurface, Point)>, - serial: Serial, - time: u32, + data: &mut State, + _dh: &DisplayHandle, + handle: &mut PointerInnerHandle<'_, State>, + event: &MotionEvent ) { // While the grab is active, no client has pointer focus - handle.motion(location, None, serial, time); - - let delta = location - self.start_data.location; - let new_location = self.initial_window_location.to_f64() + delta; - self.window - .user_data() - .insert_if_missing(|| RefCell::>::new(None)); - let data = self - .window - .user_data() - .get::>>() - .unwrap(); - *data.borrow_mut() = Some(MoveData { - new_location: new_location.to_i32_round(), - }); + handle.motion(event.location, None, event.serial, event.time); + self.delta = event.location - self.start_data.location; + + if let Some(workspace) = data.common.shell.space_for_surface_mut(self.window.toplevel().wl_surface()) { + let new_location = (self.initial_window_location.to_f64() + self.delta).to_i32_round(); + workspace.space.map_window(&self.window, new_location, true); + } } fn button( &mut self, - handle: &mut PointerInnerHandle<'_>, - button: u32, - state: ButtonState, - serial: Serial, - time: u32, + _data: &mut State, + _dh: &DisplayHandle, + handle: &mut PointerInnerHandle<'_, State>, + event: &ButtonEvent, ) { - handle.button(button, state, serial, time); + handle.button(event.button, event.state, event.serial, event.time); if handle.current_pressed().is_empty() { // No more buttons are pressed, release the grab. - handle.unset_grab(serial, time); + handle.unset_grab(event.serial, event.time); } } - fn axis(&mut self, handle: &mut PointerInnerHandle<'_>, details: AxisFrame) { + fn axis( + &mut self, + _data: &mut State, + _dh: &DisplayHandle, + handle: &mut PointerInnerHandle<'_, State>, + details: AxisFrame, + ) { handle.axis(details) } @@ -88,19 +85,9 @@ impl MoveSurfaceGrab { start_data, window, initial_window_location, + delta: (0.0, 0.0).into(), } } - - pub fn apply_move_state(window: &Window) -> Option> { - window - .user_data() - .get::>>() - .and_then(|opt| { - opt.borrow_mut() - .take() - .map(|move_data| move_data.new_location) - }) - } } bitflags::bitflags! { @@ -120,14 +107,14 @@ bitflags::bitflags! { impl From for ResizeEdge { #[inline] fn from(x: xdg_toplevel::ResizeEdge) -> Self { - Self::from_bits(x.to_raw()).unwrap() + Self::from_bits(x.into()).unwrap() } } impl From for xdg_toplevel::ResizeEdge { #[inline] fn from(x: ResizeEdge) -> Self { - Self::from_raw(x.bits()).unwrap() + Self::try_from(x.bits()).unwrap() } } @@ -169,25 +156,24 @@ pub struct ResizeSurfaceGrab { last_window_size: Size, } -impl PointerGrab for ResizeSurfaceGrab { +impl PointerGrab for ResizeSurfaceGrab { fn motion( &mut self, - handle: &mut PointerInnerHandle<'_>, - location: Point, - _focus: Option<(wl_surface::WlSurface, Point)>, - serial: Serial, - time: u32, + _data: &mut State, + _dh: &DisplayHandle, + handle: &mut PointerInnerHandle<'_, State>, + event: &MotionEvent, ) { // While the grab is active, no client has pointer focus - handle.motion(location, None, serial, time); + handle.motion(event.location, None, event.serial, event.time); // It is impossible to get `min_size` and `max_size` of dead toplevel, so we return early. - if !self.window.toplevel().alive() | self.window.toplevel().get_surface().is_none() { - handle.unset_grab(serial, time); + if !self.window.alive() { + handle.unset_grab(event.serial, event.time); return; } - let (mut dx, mut dy) = (location - self.start_data.location).into(); + let (mut dx, mut dy) = (event.location - self.start_data.location).into(); let mut new_window_width = self.initial_window_size.w; let mut new_window_height = self.initial_window_size.h; @@ -212,11 +198,10 @@ impl PointerGrab for ResizeSurfaceGrab { } let (min_size, max_size) = - with_states(self.window.toplevel().get_surface().unwrap(), |states| { + with_states(self.window.toplevel().wl_surface(), |states| { let data = states.cached_state.current::(); (data.min_size, data.max_size) - }) - .unwrap(); + }); let min_width = min_size.w.max(1); let min_height = min_size.h.max(1); @@ -238,44 +223,39 @@ impl PointerGrab for ResizeSurfaceGrab { match &self.window.toplevel() { Kind::Xdg(xdg) => { - let ret = xdg.with_pending_state(|state| { + xdg.with_pending_state(|state| { state.states.set(xdg_toplevel::State::Resizing); state.size = Some(self.last_window_size); }); - if ret.is_ok() { - xdg.send_configure(); - } + xdg.send_configure(); } } } fn button( &mut self, - handle: &mut PointerInnerHandle<'_>, - button: u32, - state: ButtonState, - serial: Serial, - time: u32, + _data: &mut State, + _dh: &DisplayHandle, + handle: &mut PointerInnerHandle<'_, State>, + event: &ButtonEvent, ) { - handle.button(button, state, serial, time); + handle.button(event.button, event.state, event.serial, event.time); if handle.current_pressed().is_empty() { // No more buttons are pressed, release the grab. - handle.unset_grab(serial, time); + handle.unset_grab(event.serial, event.time); // If toplevel is dead, we can't resize it, so we return early. - if !self.window.toplevel().alive() | self.window.toplevel().get_surface().is_none() { + if !self.window.alive() { return; } #[allow(irrefutable_let_patterns)] if let Kind::Xdg(xdg) = &self.window.toplevel() { - let ret = xdg.with_pending_state(|state| { + xdg.with_pending_state(|state| { state.states.unset(xdg_toplevel::State::Resizing); state.size = Some(self.last_window_size); }); - if ret.is_ok() { - xdg.send_configure(); - } + xdg.send_configure(); } let mut resize_state = self @@ -285,14 +265,20 @@ impl PointerGrab for ResizeSurfaceGrab { .unwrap() .borrow_mut(); if let ResizeState::Resizing(resize_data) = *resize_state { - *resize_state = ResizeState::WaitingForFinalAck(resize_data, serial); + *resize_state = ResizeState::WaitingForFinalAck(resize_data, event.serial); } else { panic!("invalid resize state: {:?}", resize_state); } } } - fn axis(&mut self, handle: &mut PointerInnerHandle<'_>, details: AxisFrame) { + fn axis( + &mut self, + _data: &mut State, + _dh: &DisplayHandle, + handle: &mut PointerInnerHandle<'_, State>, + details: AxisFrame, + ) { handle.axis(details) } @@ -334,11 +320,7 @@ impl ResizeSurfaceGrab { } pub fn ack_configure(window: &Window, configure: ToplevelConfigure) { - let surface = if let Some(surface) = window.toplevel().get_surface() { - surface - } else { - return; - }; + let surface = window.toplevel().wl_surface(); let waiting_for_serial = if let Some(data) = window.user_data().get::>() { @@ -372,8 +354,7 @@ impl ResizeSurfaceGrab { .current .states .contains(xdg_toplevel::State::Resizing) - }) - .unwrap(); + }); if configure.serial >= serial && is_resizing { let mut resize_state = window diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 5260d442..b823d060 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -1,9 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::shell::layout::Layout; use smithay::{ - desktop::{layer_map_for_output, Kind, Space, Window}, - reexports::wayland_protocols::xdg_shell::server::xdg_toplevel::{ + desktop::{layer_map_for_output, Kind, Space, Window, space::RenderZindex}, + reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::{ ResizeEdge, State as XdgState, }, wayland::{ @@ -11,7 +10,11 @@ use smithay::{ seat::{PointerGrabStartData, Seat}, Serial, }, + utils::IsAlive, }; +use std::collections::HashSet; + +use crate::state::State; mod grabs; pub use self::grabs::*; @@ -19,61 +22,94 @@ pub use self::grabs::*; #[derive(Debug, Default)] pub struct FloatingLayout { pending_windows: Vec, + pub windows: HashSet, } -impl Layout for FloatingLayout { - fn map_window<'a>( +impl FloatingLayout { + pub fn new() -> FloatingLayout { + Default::default() + } + + pub fn map_window( &mut self, space: &mut Space, - window: &Window, - seat: &Seat, - _focus_stack: Box + 'a>, + window: Window, + seat: &Seat, ) { if let Some(output) = super::output_from_seat(Some(seat), space) { - Self::map_window(space, window, &output) + self.map_window_internal(space, window, &output); } else { - self.pending_windows.push(window.clone()); + self.pending_windows.push(window); } } - fn refresh(&mut self, space: &mut Space) { + 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 self.pending_windows.drain(..) { - Self::map_window(space, &window, &output); + for window in std::mem::take(&mut self.pending_windows).into_iter() { + self.map_window_internal(space, window, &output); } } // TODO make sure all windows are still visible on any output or move them } - fn unmap_window(&mut self, space: &mut Space, window: &Window) { - space.unmap_window(window); - self.pending_windows.retain(|w| w != window); - } - - fn maximize_request(&mut self, space: &mut Space, window: &Window, output: &Output) { + fn map_window_internal( + &mut self, + space: &mut Space, + window: Window, + output: &Output + ) { + let win_geo = window.bbox(); let layers = layer_map_for_output(&output); let geometry = layers.non_exclusive_zone(); - space.map_window(&window, (-geometry.loc.x, -geometry.loc.y), true); + let position = ( + -geometry.loc.x + (geometry.size.w / 2) - (win_geo.size.w / 2), + -geometry.loc.y + (geometry.size.h / 2) - (win_geo.size.h / 2), + ); + #[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); + }); + xdg.send_configure(); + } + + window.override_z_index(RenderZindex::Shell as u8 + 1); + space.map_window(&window, position, false); + self.windows.insert(window); + } + + pub fn unmap_window(&mut self, space: &mut Space, window: &Window) { + window.clear_z_index(); + space.unmap_window(window); + self.pending_windows.retain(|w| w != window); + self.windows.remove(window); + } + + pub fn maximize_request(&mut self, space: &mut Space, window: &Window, output: &Output) { + let layers = layer_map_for_output(&output); + let geometry = layers.non_exclusive_zone(); + + space.map_window(&window, (geometry.loc.x, geometry.loc.y), true); #[allow(irrefutable_let_patterns)] if let Kind::Xdg(surface) = &window.toplevel() { - let ret = surface.with_pending_state(|state| { + surface.with_pending_state(|state| { state.states.set(XdgState::Maximized); state.size = Some(geometry.size); }); - - if ret.is_ok() { - window.configure(); - } + window.configure(); } } - fn move_request( + pub fn move_request( &mut self, space: &mut Space, window: &Window, - seat: &Seat, + seat: &Seat, serial: Serial, start_data: PointerGrabStartData, ) { @@ -83,31 +119,26 @@ impl Layout for FloatingLayout { #[allow(irrefutable_let_patterns)] if let Kind::Xdg(surface) = &window.toplevel() { // If surface is maximized then unmaximize it - if let Some(current_state) = surface.current_state() { - if current_state.states.contains(XdgState::Maximized) { - let fs_changed = surface.with_pending_state(|state| { - state.states.unset(XdgState::Maximized); - state.size = None; - }); + let current_state = surface.current_state(); + if current_state.states.contains(XdgState::Maximized) { + surface.with_pending_state(|state| { + state.states.unset(XdgState::Maximized); + state.size = None; + }); - if fs_changed.is_ok() { - surface.send_configure(); + surface.send_configure(); - // NOTE: In real compositor mouse location should be mapped to a new window size - // For example, you could: - // 1) transform mouse pointer position from compositor space to window space (location relative) - // 2) divide the x coordinate by width of the window to get the percentage - // - 0.0 would be on the far left of the window - // - 0.5 would be in middle of the window - // - 1.0 would be on the far right of the window - // 3) multiply the percentage by new window width - // 4) by doing that, drag will look a lot more natural - // - // but for anvil needs setting location to pointer location is fine - let pos = pointer.current_location(); - initial_window_location = (pos.x as i32, pos.y as i32).into(); - } - } + // TODO: The mouse location should be mapped to a new window size + // For example, you could: + // 1) transform mouse pointer position from compositor space to window space (location relative) + // 2) divide the x coordinate by width of the window to get the percentage + // - 0.0 would be on the far left of the window + // - 0.5 would be in middle of the window + // - 1.0 would be on the far right of the window + // 3) multiply the percentage by new window width + // 4) by doing that, drag will look a lot more natural + let pos = pointer.current_location(); + initial_window_location = (pos.x as i32, pos.y as i32).into(); } } @@ -117,11 +148,11 @@ impl Layout for FloatingLayout { } } - fn resize_request( + pub fn resize_request( &mut self, space: &mut Space, window: &Window, - seat: &Seat, + seat: &Seat, serial: Serial, start_data: PointerGrabStartData, edges: ResizeEdge, @@ -137,33 +168,3 @@ impl Layout for FloatingLayout { } } } - -impl FloatingLayout { - pub fn new() -> FloatingLayout { - Default::default() - } - - fn map_window(space: &mut Space, window: &Window, output: &Output) { - let win_geo = window.bbox(); - let layers = layer_map_for_output(&output); - let geometry = layers.non_exclusive_zone(); - - let position = ( - -geometry.loc.x + (geometry.size.w / 2) - (win_geo.size.w / 2), - -geometry.loc.y + (geometry.size.h / 2) - (win_geo.size.h / 2), - ); - #[allow(irrefutable_let_patterns)] - if let Kind::Xdg(xdg) = &window.toplevel() { - let ret = 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 ret.is_ok() { - xdg.send_configure(); - } - } - space.map_window(&window, position, false); - } -} diff --git a/src/shell/layout/mod.rs b/src/shell/layout/mod.rs index 16838c25..f2ab6314 100644 --- a/src/shell/layout/mod.rs +++ b/src/shell/layout/mod.rs @@ -1,19 +1,20 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::input::ActiveOutput; + +use crate::{ + input::ActiveOutput, + state::State, +}; use smithay::{ desktop::{Space, Window}, - reexports::wayland_protocols::xdg_shell::server::xdg_toplevel::ResizeEdge, wayland::{ compositor::with_states, output::Output, - seat::{PointerGrabStartData, Seat}, + seat::Seat, shell::xdg::XdgToplevelSurfaceRoleAttributes, - Serial, }, }; use std::sync::Mutex; -pub mod combined; pub mod floating; pub mod tiling; @@ -23,112 +24,38 @@ pub enum Orientation { Vertical, } -#[derive(Debug, serde::Deserialize, Clone, Copy, PartialEq, Eq)] -pub enum FocusDirection { - Left, - Right, - Up, - Down, +pub fn should_be_floating(window: &Window) -> bool { + let surface = window.toplevel().wl_surface(); + with_states(surface, |states| { + let attrs = states + .data_map + .get::>() + .unwrap() + .lock() + .unwrap(); + + // simple heuristic taken from + // sway/desktop/xdg_shell.c:188 @ 0ee54a52 + if attrs.parent.is_some() + || (attrs.min_size.w != 0 + && attrs.min_size.h != 0 + && attrs.min_size == attrs.max_size) + { + return true; + } + + // else take a look at our exceptions + match ( + attrs.app_id.as_deref().unwrap_or(""), + attrs.title.as_deref().unwrap_or(""), + ) { + ("gcr-prompter", _) => true, + _ => false, + } + }) } -pub trait Layout { - fn map_window<'a>( - &mut self, - space: &mut Space, - window: &Window, - seat: &Seat, - focus_stack: Box + 'a>, - ); //working around object safety.. - fn refresh(&mut self, space: &mut Space); - fn unmap_window(&mut self, space: &mut Space, window: &Window); - - fn move_focus<'a>( - &mut self, - direction: FocusDirection, - seat: &Seat, - space: &mut Space, - focus_stack: Box + 'a>, - ) -> Option { - let _ = (direction, seat, space, focus_stack); - None - } - fn update_orientation<'a>( - &mut self, - orientation: Orientation, - seat: &Seat, - space: &mut Space, - focus_stack: Box + 'a>, - ) { - let _ = (orientation, seat, space, focus_stack); - } - fn maximize_request(&mut self, space: &mut Space, window: &Window, output: &Output) { - let _ = (space, window, output); - } - fn move_request( - &mut self, - space: &mut Space, - window: &Window, - seat: &Seat, - serial: Serial, - start_data: PointerGrabStartData, - ) { - let _ = (space, window, seat, serial, start_data); - } - fn resize_request( - &mut self, - space: &mut Space, - window: &Window, - seat: &Seat, - serial: Serial, - start_data: PointerGrabStartData, - edges: ResizeEdge, - ) { - let _ = (space, window, seat, serial, start_data, edges); - } -} - -pub fn new_default_layout() -> Box { - Box::new(combined::Combined::new( - tiling::TilingLayout::new(), - floating::FloatingLayout::new(), - |window| { - if let Some(surface) = window.toplevel().get_surface() { - with_states(surface, |states| { - let attrs = states - .data_map - .get::>() - .unwrap() - .lock() - .unwrap(); - - // simple heuristic taken from - // sway/desktop/xdg_shell.c:188 @ 0ee54a52 - if attrs.parent.is_some() - || (attrs.min_size.w != 0 - && attrs.min_size.h != 0 - && attrs.min_size == attrs.max_size) - { - return true; - } - - // else take a look at our exceptions - match ( - attrs.app_id.as_deref().unwrap_or(""), - attrs.title.as_deref().unwrap_or(""), - ) { - ("gcr-prompter", _) => true, - _ => false, - } - }) - .unwrap_or(false) - } else { - false - } - }, - )) -} - -fn output_from_seat(seat: Option<&Seat>, space: &Space) -> Option { +fn output_from_seat(seat: Option<&Seat>, space: &Space) -> Option { seat.and_then(|seat| { seat.user_data() .get::() diff --git a/src/shell/layout/tiling/grabs.rs b/src/shell/layout/tiling/grabs.rs index da7de702..fddeb9cd 100644 --- a/src/shell/layout/tiling/grabs.rs +++ b/src/shell/layout/tiling/grabs.rs @@ -1,13 +1,16 @@ // SPDX-License-Identifier: GPL-3.0-only -use super::Orientation; + +use crate::{ + utils::prelude::*, + shell::layout::Orientation, +}; use atomic_float::AtomicF64; use smithay::{ - reexports::wayland_server::protocol::{wl_pointer::ButtonState, wl_surface}, - utils::{Logical, Point, Size}, - wayland::{ - seat::{AxisFrame, PointerGrab, PointerGrabStartData, PointerInnerHandle}, - Serial, + reexports::wayland_server::{ + DisplayHandle, }, + utils::{Logical, Size}, + wayland::seat::{AxisFrame, PointerGrab, PointerGrabStartData, PointerInnerHandle, MotionEvent, ButtonEvent}, }; use std::sync::{atomic::Ordering, Arc}; @@ -19,19 +22,18 @@ pub struct ResizeForkGrab { pub ratio: Arc, } -impl PointerGrab for ResizeForkGrab { +impl PointerGrab for ResizeForkGrab { fn motion( &mut self, - handle: &mut PointerInnerHandle<'_>, - location: Point, - _focus: Option<(wl_surface::WlSurface, Point)>, - serial: Serial, - time: u32, + _data: &mut State, + _dh: &DisplayHandle, + handle: &mut PointerInnerHandle<'_, State>, + event: &MotionEvent ) { // While the grab is active, no client has pointer focus - handle.motion(location, None, serial, time); + handle.motion(event.location, None, event.serial, event.time); - let delta = location - self.start_data.location; + let delta = event.location - self.start_data.location; let delta = match self.orientation { Orientation::Vertical => delta.x / self.initial_size.w as f64, Orientation::Horizontal => delta.y / self.initial_size.h as f64, @@ -44,20 +46,24 @@ impl PointerGrab for ResizeForkGrab { fn button( &mut self, - handle: &mut PointerInnerHandle<'_>, - button: u32, - state: ButtonState, - serial: Serial, - time: u32, + _data: &mut State, + _dh: &DisplayHandle, + handle: &mut PointerInnerHandle<'_, State>, + event: &ButtonEvent, ) { - handle.button(button, state, serial, time); + handle.button(event.button, event.state, event.serial, event.time); if handle.current_pressed().is_empty() { // No more buttons are pressed, release the grab. - handle.unset_grab(serial, time); + handle.unset_grab(event.serial, event.time); } } - fn axis(&mut self, handle: &mut PointerInnerHandle<'_>, details: AxisFrame) { + fn axis(&mut self, + _data: &mut State, + _dh: &DisplayHandle, + handle: &mut PointerInnerHandle<'_, State>, + details: AxisFrame, + ) { handle.axis(details) } diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 91a5a982..3a4102cb 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -1,14 +1,21 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::shell::layout::{FocusDirection, Layout, Orientation}; +use crate::{ + shell::{ + layout::Orientation, + focus::FocusDirection, + }, + 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}, - reexports::wayland_protocols::xdg_shell::server::xdg_toplevel::{ + reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::{ ResizeEdge, State as XdgState, }, - utils::Rectangle, + utils::{IsAlive, Rectangle}, wayland::{ seat::{PointerGrabStartData, Seat}, Serial, @@ -16,6 +23,7 @@ use smithay::{ }; use std::{ cell::RefCell, + collections::HashSet, sync::{atomic::Ordering, Arc}, }; @@ -26,6 +34,7 @@ pub use self::grabs::*; pub struct TilingLayout { gaps: (i32, i32), trees: Vec>, + pub windows: HashSet, } #[derive(Debug)] @@ -61,28 +70,30 @@ impl TilingLayout { TilingLayout { gaps: (0, 4), trees: Vec::new(), + windows: HashSet::new(), } } } -impl Layout for TilingLayout { - fn map_window<'a>( +impl TilingLayout { + pub fn map_window<'a>( &mut self, space: &mut Space, - window: &Window, - seat: &Seat, - focus_stack: Box + 'a>, + window: Window, + seat: &Seat, + focus_stack: impl Iterator + 'a, ) { - self.map_window(space, window, Some(seat), Some(focus_stack)); + self.map_window_internal(space, &window, Some(seat), Some(focus_stack)); + self.windows.insert(window); self.refresh(space); } - fn move_focus<'a>( + pub fn move_focus<'a>( &mut self, direction: FocusDirection, - seat: &Seat, + seat: &Seat, space: &mut Space, - focus_stack: Box + 'a>, + focus_stack: impl Iterator + 'a, ) -> Option { let output = super::output_from_seat(Some(seat), space); let idx = space @@ -136,12 +147,13 @@ impl Layout for TilingLayout { None } - fn update_orientation<'a>( + + pub fn update_orientation<'a>( &mut self, new_orientation: Orientation, - seat: &Seat, + seat: &Seat, space: &mut Space, - focus_stack: Box + 'a>, + focus_stack: impl Iterator + 'a, ) { let output = super::output_from_seat(Some(seat), space); let idx = space @@ -163,7 +175,7 @@ impl Layout for TilingLayout { self.refresh(space); } - fn refresh<'a>(&mut self, space: &mut Space) { + 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 @@ -175,7 +187,7 @@ impl Layout for TilingLayout { 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(space, window, None, None); + self.map_window_internal(space, window, None, Option::>::None); } } } @@ -189,22 +201,32 @@ impl Layout for TilingLayout { .filter(|v| !v.is_empty()) { for window in dead_windows { - self.unmap_window(&window); + self.unmap_window_internal(&window); } } } - fn unmap_window(&mut self, space: &mut Space, window: &Window) { - self.unmap_window(&window); - space.unmap_window(&window); + 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); } - fn resize_request( + pub fn resize_request( &mut self, space: &mut Space, window: &Window, - seat: &Seat, + seat: &Seat, serial: Serial, start_data: PointerGrabStartData, edges: ResizeEdge, @@ -252,9 +274,7 @@ impl Layout for TilingLayout { } } } -} - -impl TilingLayout { + fn active_tree<'a>(trees: &'a mut Vec>, output: usize) -> &'a mut Tree { while trees.len() <= output { trees.push(Tree::new()) @@ -286,12 +306,12 @@ impl TilingLayout { None } - fn map_window<'a>( + fn map_window_internal<'a>( &mut self, space: &mut Space, window: &Window, - seat: Option<&Seat>, - focus_stack: Option + 'a>>, + seat: Option<&Seat>, + focus_stack: Option + 'a>, ) { let output = super::output_from_seat(seat, space); let idx = space @@ -353,7 +373,7 @@ impl TilingLayout { } } - fn unmap_window(&mut self, window: &Window) { + 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); @@ -534,25 +554,21 @@ impl TilingLayout { } } Data::Window(window) => { - if window.toplevel().alive() { + if window.alive() { if let Some(geo) = geo { #[allow(irrefutable_let_patterns)] if let Kind::Xdg(xdg) = &window.toplevel() { if xdg .current_state() - .map(|state| { - state.states.contains(XdgState::Fullscreen) + .states.contains(XdgState::Fullscreen) + || xdg + .with_pending_state(|pending| { + pending.states.contains(XdgState::Fullscreen) }) - .unwrap_or(false) - || xdg - .with_pending_state(|pending| { - pending.states.contains(XdgState::Fullscreen) - }) - .unwrap_or(false) { continue; } - let ret = xdg.with_pending_state(|state| { + xdg.with_pending_state(|state| { state.size = Some( (geo.size.w - inner * 2, geo.size.h - inner * 2) .into(), @@ -562,9 +578,7 @@ impl TilingLayout { state.states.set(XdgState::TiledTop); state.states.set(XdgState::TiledBottom); }); - if ret.is_ok() { - xdg.send_configure(); - } + xdg.send_configure(); } space.map_window( &window, diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 5634f07b..e5043ad3 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1,600 +1,337 @@ -// SPDX-License-Identifier: GPL-3.0-only - -#[cfg(feature = "experimental")] -use crate::wayland::workspace as ext_work; -use crate::{ - config::{Config, OutputConfig}, - input::{active_output, set_active_output}, - state::Common, +use std::{ + cell::Cell, + mem::MaybeUninit, }; -pub use smithay::{ - desktop::{layer_map_for_output, PopupGrab, PopupManager, PopupUngrabStrategy, Space, Window, WindowSurfaceType}, - reexports::wayland_server::{protocol::wl_surface::WlSurface, Display}, - utils::{Logical, Point, Rectangle, Size}, + +use smithay::{ + desktop::{ + LayerSurface, + PopupManager, + Window, + WindowSurfaceType, + layer_map_for_output, + }, + reexports::wayland_server::{ + DisplayHandle, + protocol::wl_surface::WlSurface, + }, wayland::{ compositor::with_states, - output::{Mode as OutputMode, Output, Scale}, - seat::Seat, + seat::{Seat, MotionEvent}, shell::{ - wlr_layer::{KeyboardInteractivity, Layer, LayerSurfaceCachedState}, - xdg::XdgToplevelSurfaceRoleAttributes, + wlr_layer::{WlrLayerShellState, Layer, LayerSurfaceCachedState, KeyboardInteractivity}, + xdg::XdgShellState, }, - Serial, SERIAL_COUNTER, + output::Output, + SERIAL_COUNTER, }, + utils::{Point, Rectangle, Logical}, }; -use std::{ - cell::{Cell, RefCell}, - mem::MaybeUninit, - sync::Mutex, +use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::State as WState; + +use crate::{ + config::{Config, WorkspaceMode as ConfigMode}, + wayland::protocols::{ + toplevel_info::ToplevelInfoState, + workspace::{ + WorkspaceState, + WorkspaceGroupHandle, + WorkspaceHandle, + WorkspaceCapabilities, + WorkspaceUpdateGuard, + }, + }, + utils::prelude::*, }; -pub const MAX_WORKSPACES: usize = 10; // TODO? - -mod handler; +pub const MAX_WORKSPACES: usize = 10; pub mod layout; mod workspace; -pub use self::handler::{init_shell, PopupGrabData}; -pub use self::layout::Layout; +pub mod focus; pub use self::workspace::*; -pub struct ActiveWorkspace(Cell>); -impl ActiveWorkspace { - fn new() -> Self { - ActiveWorkspace(Cell::new(None)) - } - pub fn get(&self) -> Option { - self.0.get() - } - fn set(&self, active: usize) -> Option { - self.0.replace(Some(active)) - } - fn clear(&self) -> Option { - self.0.replace(None) - } +pub struct Shell { + pub popups: PopupManager, + pub spaces: [Workspace; MAX_WORKSPACES], + pub outputs: Vec, + pub workspace_mode: WorkspaceMode, + pub shell_mode: ShellMode, + + pub pending_windows: Vec<(Window, Seat)>, + pub pending_layers: Vec<(LayerSurface, Output, Seat)>, + + // wayland_state + pub layer_shell_state: WlrLayerShellState, + pub toplevel_info_state: ToplevelInfoState, + pub xdg_shell_state: XdgShellState, + pub workspace_state: WorkspaceState, } -#[derive(Debug, serde::Deserialize, PartialEq, Eq, Clone, Copy)] -pub enum Mode { +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum WorkspaceMode { OutputBound, Global { - #[serde(default)] active: usize, + group: WorkspaceGroupHandle, }, } -impl Mode { - pub fn output_bound() -> Mode { - Mode::OutputBound - } - - pub fn global() -> Mode { - Mode::Global { active: 0 } - } +pub enum ShellMode { + Normal, + Resize, + Adjust, } -pub struct Shell { - popups: PopupManager, - mode: Mode, - outputs: Vec, - pub spaces: [Workspace; MAX_WORKSPACES], - #[cfg(feature = "experimental")] - manager: ext_work::WorkspaceManager, - #[cfg(feature = "experimental")] - global_group: Option, +#[derive(Debug, Clone)] +pub struct OutputBoundState { + active: Cell, + group: Cell, } const UNINIT_SPACE: MaybeUninit = MaybeUninit::uninit(); impl Shell { - fn new(config: &Config, _display: &mut Display) -> Self { + pub fn new(config: &Config, dh: &DisplayHandle) -> Self { + let layer_shell_state = WlrLayerShellState::new::(dh, None); + let toplevel_info_state = ToplevelInfoState::new(dh, |_| true); + let xdg_shell_state = XdgShellState::new::(dh, None); + let mut workspace_state = WorkspaceState::new(dh, |_| 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); + Shell { popups: PopupManager::new(None), - mode: config.static_conf.workspace_mode, + spaces, outputs: Vec::new(), - 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::transmute(spaces) - }, - #[cfg(feature = "experimental")] - manager: ext_work::init_ext_workspace( - _display, - |_, changes, mut ddata| { - let state = ddata.get::().unwrap(); - if let Some((w, _)) = changes.into_iter().rev().find(|(_, p)| { - p.into_iter() - .rev() - .find(|o| { - **o == ext_work::PendingOperation::Activate - || **o == ext_work::PendingOperation::Deactivate - }) - .map(|o| *o == ext_work::PendingOperation::Activate) - .unwrap_or(false) - }) { - if let Some(idx) = state.common.shell.spaces.iter().position(|work| { - work.ext_workspace.as_ref().map(|e| e == w).unwrap_or(false) - }) { - state.common.event_loop_handle.insert_idle(move |state| { - let seat = &state.common.last_active_seat; - let output = active_output(&seat, &state.common); - state.common.shell.activate(seat, &output, idx); - }); - } - } - }, - |_| true, - ) - .0, - #[cfg(feature = "experimental")] - global_group: None, + workspace_mode: mode, + shell_mode: ShellMode::Normal, + + pending_windows: Vec::new(), + pending_layers: Vec::new(), + + layer_shell_state, + toplevel_info_state, + xdg_shell_state, + workspace_state, } } + 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); + } + } + + 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()); + } + }, + 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); + } + } + } + + pub fn remove_output(&mut self, output: &Output) { + 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); + + // 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); + } + } + } + + // 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); + }, + }; + } + pub fn refresh_outputs(&mut self) { - if let Mode::Global { active } = self.mode { + if let WorkspaceMode::Global { active, .. } = self.workspace_mode { let workspace = &mut self.spaces[active]; for output in self.outputs.iter() { - let config = output - .user_data() - .get::>() - .unwrap() - .borrow(); - workspace.space.map_output(output, config.position); + workspace.space.map_output(output, output.current_location()); } } else { for output in self.outputs.iter() { let active = output .user_data() - .get::() + .get::() .unwrap() - .get() - .unwrap(); + .active + .get(); let workspace = &mut self.spaces[active]; workspace.space.map_output(output, (0, 0)); } } } - fn assign_next_free_output<'a>( - spaces: &'a mut [Workspace], - output: &Output, - ) -> (usize, &'a mut Workspace) { - output - .user_data() - .insert_if_missing(|| ActiveWorkspace::new()); - let (idx, workspace) = spaces - .iter_mut() - .enumerate() - .find(|(_, x)| x.space.outputs().next().is_none()) - .expect("More then 10 outputs?"); - output - .user_data() - .get::() - .unwrap() - .set(idx); - - (idx, workspace) + 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); + 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); + } + }, + (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 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); + } + } + }, + _ => {}, + } } - pub fn add_output(&mut self, output: &Output) { - self.outputs.push(output.clone()); - let config = output - .user_data() - .get::>() - .unwrap() - .borrow(); + pub fn activate(&mut self, _dh: &DisplayHandle, seat: &Seat, output: &Output, idx: usize) -> Option { + if idx > MAX_WORKSPACES { + return None; + } - match self.mode { - Mode::OutputBound => { - let _idx = { - let (idx, workspace) = Self::assign_next_free_output(&mut self.spaces, output); - workspace.space.map_output(output, (0, 0)); - idx - }; - #[cfg(feature = "experimental")] - { - let group = self - .manager - .new_group(|_, _, _| {}, std::iter::once(output.clone())); - if self.spaces.iter().all(|w| w.ext_workspace.is_none()) { - for (idx, workspace) in self.spaces.iter_mut().enumerate() { - let ext_workspace = group.create_workspace(format!("{}", idx + 1)); - ext_workspace.set_coordinates(std::iter::once(idx as u32)); - workspace.ext_workspace = Some(ext_workspace); - } - self.spaces[_idx] - .ext_workspace - .as_mut() - .unwrap() - .add_state(ext_work::State::Active); - } else { - let ext_workspace = group.create_workspace(format!("{}", _idx + 1)); - ext_workspace.set_coordinates(std::iter::once(_idx as u32)); - ext_workspace.add_state(ext_work::State::Active); - if let Some(old) = self.spaces[_idx].ext_workspace.replace(ext_workspace) { - old.remove(); - } - } - output.user_data().insert_if_missing(|| { - RefCell::new(Option::::None) - }); - *output - .user_data() - .get::>>() - .unwrap() - .borrow_mut() = Some(group); - self.manager.done(); - } - } - Mode::Global { active } => { - #[cfg(feature = "experimental")] - { - if self.global_group.is_none() { - self.global_group = Some( - self.manager - .new_group(|_, _, _| {}, std::iter::once(output.clone())), - ); - for (idx, workspace) in self.spaces.iter_mut().enumerate() { - let ext_workspace = self - .global_group - .as_mut() - .unwrap() - .create_workspace(format!("{}", idx + 1)); - ext_workspace.set_coordinates(std::iter::once(idx as u32)); - if idx == active { - ext_workspace.add_state(ext_work::State::Active); - } - workspace.ext_workspace = Some(ext_workspace); - } - } else { - self.manager.update_outputs(|_, outputs| { - outputs.insert(output.clone()); + 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(), + focus: None, // This should actually be a surface, if there is one in the center + serial: SERIAL_COUNTER.next_serial(), + time: 0, }); } - self.manager.done(); } - let workspace = &mut self.spaces[active]; - workspace.space.map_output(output, config.position); - } - } - output.change_current_state(None, None, Some(Scale::Fractional(config.scale)), None); - } + // 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); - pub fn remove_output(&mut self, output: &Output) { - match self.mode { - Mode::OutputBound => { - if let Some(idx) = output - .user_data() - .get::() - .and_then(|a| a.get()) - { - self.spaces[idx].space.unmap_output(output); - self.outputs.retain(|o| o != output); - } - #[cfg(feature = "experimental")] - { - let group = output - .user_data() - .get::>>() - .unwrap() - .borrow_mut() - .take() - .unwrap(); - let mut alternative_group = self.outputs.last_mut().map(|o| { - o.user_data() - .get::>>() - .unwrap() - }); - for (idx, workspace) in self.spaces.iter_mut().enumerate() { - if workspace - .ext_workspace - .as_ref() - .map(|w| group.belongs(w)) - .unwrap_or(false) - { - workspace.ext_workspace.take().unwrap().remove(); - if let Some(alternative) = alternative_group.as_mut() { - let ext_workspace = alternative - .borrow_mut() - .as_mut() - .unwrap() - .create_workspace(format!("{}", idx + 1)); - ext_workspace.set_coordinates(std::iter::once(idx as u32)); - workspace.ext_workspace = Some(ext_workspace); - } - } + 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]); } - self.manager.remove_group(&group); + + 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); } - } - Mode::Global { active } => { - #[cfg(feature = "experimental")] - self.manager.update_outputs(|_, outputs| { - outputs.remove(output); - }); - self.spaces[active].space.unmap_output(output); - self.outputs.retain(|o| o != output); - // TODO move windows and outputs farther on the right / or load save config for remaining monitors - } - } - } - - pub fn output_size(&self, output: &Output) -> Size { - let workspace = self.active_space(output); - workspace - .space - .output_geometry(&output) - .unwrap_or(Rectangle::from_loc_and_size((0, 0), (0, 0))) - .size - } - - pub fn global_space(&self) -> Rectangle { - self.outputs - .iter() - .fold( - Option::>::None, - |maybe_geo, output| match maybe_geo { - Some(rect) => Some(rect.merge(self.output_geometry(output))), - None => Some(self.output_geometry(output)), - }, - ) - .unwrap_or_else(|| Rectangle::from_loc_and_size((0, 0), (0, 0))) - } - - pub fn space_relative_output_geometry( - &self, - global_loc: impl Into>, - output: &Output, - ) -> Point { - match self.mode { - Mode::Global { .. } => global_loc.into(), - Mode::OutputBound => { - let p = global_loc.into().to_f64() - self.output_geometry(output).loc.to_f64(); - (C::from_f64(p.x), C::from_f64(p.y)).into() - } - } - } - - pub fn output_geometry(&self, output: &Output) -> Rectangle { - // due to our different modes, we cannot just ask the space for the global output coordinates, - // because for `Mode::OutputBound` the origin will always be (0, 0) - - let config = output - .user_data() - .get::>() - .unwrap() - .borrow(); - Rectangle::from_loc_and_size(config.position, self.output_size(output)) - } - - pub fn activate(&mut self, seat: &Seat, output: &Output, idx: usize) { - if idx > MAX_WORKSPACES { - return; - } - - match self.mode { - Mode::OutputBound => { - for output in &self.outputs { - if output - .user_data() - .get::() - .and_then(|i| i.get().map(|i| i == idx)) - .unwrap_or(false) - { - let geometry = self.output_geometry(output); - if let Some(ptr) = seat.get_pointer() { - ptr.motion( - Point::::from(( - geometry.loc.x + (geometry.size.w / 2), - geometry.loc.y + (geometry.size.h / 2), - )) - .to_f64(), - None, - SERIAL_COUNTER.next_serial(), - 0, - ); - set_active_output(seat, output); - return; - } - } - } - - if let Some(active) = output.user_data().get::() { - if let Some(old_idx) = active.set(idx) { - #[cfg(feature = "experimental")] - if let Some(ext_workspace) = self.spaces[old_idx].ext_workspace.as_mut() { - ext_workspace.remove_state(ext_work::State::Active); - } - self.spaces[old_idx].space.unmap_output(output); - } - self.spaces[idx].space.map_output(output, (0, 0)); - #[cfg(feature = "experimental")] - { - let mut group_borrow = output - .user_data() - .get::>>() - .unwrap() - .borrow_mut(); - let group = group_borrow.as_mut().unwrap(); - if self.spaces[idx] - .ext_workspace - .as_ref() - .map(|x| !group.belongs(x)) - .unwrap_or(true) - { - let ext_workspace = group.create_workspace(format!("{}", idx + 1)); - ext_workspace.set_coordinates(std::iter::once(idx as u32)); - if let Some(old) = self.spaces[idx].ext_workspace.replace(ext_workspace) - { - old.remove(); - } - } - self.spaces[idx] - .ext_workspace - .as_mut() - .unwrap() - .add_state(ext_work::State::Active); - self.manager.done(); - } - self.spaces[idx].refresh(); - } - } - Mode::Global { ref mut active } => { + }, + WorkspaceMode::Global { ref mut active, .. } => { let old = *active; *active = idx; - for output in &self.outputs { - self.spaces[old].space.unmap_output(output); - let config = output - .user_data() - .get::>() - .unwrap() - .borrow(); - self.spaces[*active] - .space - .map_output(output, config.position); - } - #[cfg(feature = "experimental")] - { - for (idx, workspace) in self.spaces.iter_mut().enumerate() { - if idx == *active { - workspace - .ext_workspace - .as_mut() - .unwrap() - .add_state(ext_work::State::Active); - } else { - workspace - .ext_workspace - .as_mut() - .unwrap() - .remove_state(ext_work::State::Active); - } - } - self.manager.done(); - } - } - }; - } - pub fn move_current_window(&mut self, seat: &Seat, output: &Output, idx: usize) { - if idx > MAX_WORKSPACES { - return; + 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); + } } - let workspace = self.active_space_mut(output); - if idx == workspace.idx as usize { - return; - } - - let maybe_window = workspace.focus_stack(seat).last(); - if let Some(window) = maybe_window { - workspace.unmap_window(&window); - self.spaces[idx].map_window(&window, seat); - } - } - - #[cfg(feature = "debug")] - pub fn mode(&self) -> &Mode { - &self.mode - } - - pub fn set_mode(&mut self, mode: Mode) { - match (&mut self.mode, mode) { - (Mode::OutputBound, Mode::Global { .. }) => { - let active = self - .outputs - .iter() - .next() - .map(|o| { - o.user_data() - .get::() - .unwrap() - .get() - .unwrap() - }) - .unwrap_or(0); - - for output in &self.outputs { - let old_active = output - .user_data() - .get::() - .unwrap() - .clear() - .unwrap(); - let config = output - .user_data() - .get::>() - .unwrap() - .borrow(); - self.spaces[old_active].space.unmap_output(output); - self.spaces[active] - .space - .map_output(output, config.position); - self.spaces[active].refresh(); - } - - self.mode = Mode::Global { active }; - } - (Mode::Global { active }, new @ Mode::OutputBound) => { - for output in &self.outputs { - self.spaces[*active].space.unmap_output(output); - } - - let mut active = Some(active.clone()); - self.mode = new; - for output in &self.outputs { - let workspace = if let Some(a) = active.take() { - output - .user_data() - .insert_if_missing(|| ActiveWorkspace::new()); - output.user_data().get::().unwrap().set(a); - &mut self.spaces[a] - } else { - Self::assign_next_free_output(&mut self.spaces, output).1 - }; - workspace.space.map_output(output, (0, 0)); - workspace.refresh(); - } - } - _ => {} - }; - } - - pub fn outputs(&self) -> impl Iterator { - self.outputs.iter() + None } pub fn active_space(&self, output: &Output) -> &Workspace { - match &self.mode { - Mode::OutputBound => { + match &self.workspace_mode { + WorkspaceMode::OutputBound => { let active = output .user_data() - .get::() + .get::() .unwrap() - .get() - .unwrap(); + .active + .get(); &self.spaces[active] } - Mode::Global { active } => &self.spaces[*active], + WorkspaceMode::Global { active, .. } => &self.spaces[*active], } } pub fn active_space_mut(&mut self, output: &Output) -> &mut Workspace { - match &self.mode { - Mode::OutputBound => { + match &self.workspace_mode { + WorkspaceMode::OutputBound => { let active = output .user_data() - .get::() + .get::() .unwrap() - .get() - .unwrap(); + .active + .get(); &mut self.spaces[active] } - Mode::Global { active } => &mut self.spaces[*active], + WorkspaceMode::Global { active, .. } => &mut self.spaces[*active], } } @@ -609,11 +346,7 @@ impl Shell { pub fn space_for_surface(&self, surface: &WlSurface) -> Option<&Workspace> { self.spaces.iter().find(|workspace| { - workspace - .pending_windows - .iter() - .any(|(w, _)| w.toplevel().get_surface() == Some(surface)) - || workspace.space.window_for_surface(surface, WindowSurfaceType::ALL).is_some() + workspace.space.window_for_surface(surface, WindowSurfaceType::ALL).is_some() }) } @@ -622,284 +355,256 @@ impl Shell { .iter_mut() .find(|workspace| workspace.space.window_for_surface(surface, WindowSurfaceType::ALL).is_some()) } + + pub fn outputs(&self) -> impl Iterator { + self.outputs.iter() + } - pub fn refresh(&mut self) { + pub fn global_space(&self) -> Rectangle { + self.outputs + .iter() + .fold( + Option::>::None, + |maybe_geo, output| match maybe_geo { + Some(rect) => Some(rect.merge(output.geometry())), + None => Some(output.geometry()), + }, + ) + .unwrap_or_else(|| Rectangle::from_loc_and_size((0, 0), (0, 0))) + } + + pub fn space_relative_output_geometry( + &self, + global_loc: impl Into>, + output: &Output, + ) -> Point { + match self.workspace_mode { + 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) { self.popups.cleanup(); - for output in self.outputs.iter() { - let workspace = match &self.mode { - Mode::OutputBound => { + let mut state = self.workspace_state.update(); + for output in &self.outputs { + let workspace = match &self.workspace_mode { + WorkspaceMode::OutputBound => { let active = output .user_data() - .get::() + .get::() .unwrap() - .get() - .unwrap(); + .active + .get(); &mut self.spaces[active] } - Mode::Global { active } => &mut self.spaces[*active], + WorkspaceMode::Global { active, .. } => &mut self.spaces[*active], }; - workspace.refresh(); + workspace.refresh(dh); + if workspace.space.windows().next().is_none() { + state.add_workspace_state(&workspace.handle, WState::Hidden); + } let mut map = layer_map_for_output(output); map.cleanup(); - map.arrange(); } + std::mem::drop(state); + self.toplevel_info_state.refresh(Some(&self.workspace_state)); } + + pub fn map_window(&mut self, window: &Window, output: &Output) { + let pos = self.pending_windows.iter().position(|(w, _)| w == window).unwrap(); + let (window, seat) = self.pending_windows.remove(pos); - pub fn commit<'a>(&mut self, surface: &WlSurface, seats: impl Iterator) { - let mut new_focus = None; - for (idx, workspace) in self.spaces.iter_mut().enumerate() { - if let Some((window, seat)) = workspace - .pending_windows - .iter() - .find(|(w, _)| w.toplevel().get_surface() == Some(surface)) - .cloned() - { - workspace.map_window(&window, &seat); - if match self.mode { - Mode::OutputBound => self.outputs.iter().any(|o| { - o.user_data() - .get::() - .unwrap() - .get() - .unwrap() - == idx - }), - Mode::Global { active } => active == idx, - } { - new_focus = Some(seat); - } - workspace.pending_windows.retain(|(w, _)| w != &window); - } - if let Some((layer, output, seat)) = workspace - .pending_layers - .iter() - .find(|(l, _, _)| l.get_surface() == Some(surface)) - .cloned() - { - let focus = layer - .get_surface() - .map(|surface| { - with_states(surface, |states| { - let state = states.cached_state.current::(); - matches!(state.layer, Layer::Top | Layer::Overlay) - && dbg!(state.keyboard_interactivity) != KeyboardInteractivity::None - }) - .unwrap() - }) - .unwrap_or(false); - - let mut map = layer_map_for_output(&output); - map.map_layer(&layer).unwrap(); - - if focus { - new_focus = Some(seat); - } - workspace.pending_layers.retain(|(l, _, _)| l != &layer); - } - workspace.space.commit(surface); - } - if let Some(seat) = new_focus { - self.set_focus(Some(surface), &seat, seats, None) - } - self.popups.commit(&surface); - } - - pub fn set_orientation( - &mut self, - seat: &Seat, - output: &Output, - orientation: layout::Orientation, - ) { - self.active_space_mut(output) - .update_orientation(seat, orientation) - } - - pub fn move_focus<'a>( - &mut self, - seat: &Seat, - output: &Output, - focus: layout::FocusDirection, - seats: impl Iterator, - ) { - if let Some(surface) = self - .active_space_mut(output) - .move_focus(seat, focus) - .and_then(|window| window.toplevel().get_surface().cloned()) - { - self.set_focus(Some(&surface), seat, seats, None) - } - } - - fn set_focus<'a>( - &mut self, - surface: Option<&WlSurface>, - active_seat: &Seat, - seats: impl Iterator, - serial: Option, - ) { - // update FocusStack and notify layouts about new focus (if any window) - if let Some(surface) = surface { - if let Some(workspace) = self.space_for_surface_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); - } - } - } - } - } - } - - // update keyboard focus - if let Some(keyboard) = active_seat.get_keyboard() { - ActiveFocus::set(active_seat, surface.cloned()); - keyboard.set_focus( - surface, - serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()), - ); - } - - self.update_active(seats) - } - - fn update_active<'a>(&mut self, seats: impl Iterator) { - // 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()) - }) - .collect::>(); - - for output in self.outputs.iter() { - let workspace = match &self.mode { - Mode::OutputBound => { - let active = output - .user_data() - .get::() - .unwrap() - .get() - .unwrap(); - &mut self.spaces[active] - } - Mode::Global { active } => &mut self.spaces[*active], - }; - for focused in focused_windows.iter() { - workspace.space.raise_window(focused, true); - } - for window in workspace.space.windows() { - window.set_activated(focused_windows.contains(window)); - window.configure(); - } - } - } -} - -pub struct ActiveFocus(RefCell>); - -impl ActiveFocus { - fn set(seat: &Seat, surface: Option) { - if !seat - .user_data() - .insert_if_missing(|| ActiveFocus(RefCell::new(surface.clone()))) - { - *seat - .user_data() - .get::() - .unwrap() - .0 - .borrow_mut() = surface; - } - } - - fn get(seat: &Seat) -> Option { - seat.user_data() - .get::() - .and_then(|a| a.0.borrow().clone()) - } -} - -impl Common { - pub fn set_focus( - &mut self, - surface: Option<&WlSurface>, - active_seat: &Seat, - serial: Option, - ) { - self.shell - .set_focus(surface, active_seat, self.seats.iter(), serial) - } - - pub fn refresh_focus(&mut self) { - for seat in &self.seats { - let mut fixup = false; - let output = active_output(seat, &self); - let last_known_focus = ActiveFocus::get(seat); - - if let Some(surface) = last_known_focus { - if surface.as_ref().is_alive() { - let is_toplevel = with_states(&surface, |states| { - states - .data_map - .get::>() - .is_some() - }) - .unwrap_or(false); - if !is_toplevel { - continue; - } - - let workspace = self.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) { - fixup = true; - } - } else { - fixup = true; - } - } else { - fixup = true; - } - } - - if fixup { - // also remove popup grabs, if we are switching focus - if let Some(mut popup_grab) = seat + let workspace = match &self.workspace_mode { + WorkspaceMode::OutputBound => { + let active = output .user_data() - .get::() - .and_then(|x| x.take()) - { - if !popup_grab.has_ended() { - popup_grab.ungrab(PopupUngrabStrategy::All); - } - } - - // update keyboard focus - let surface = self - .shell - .active_space(&output) - .focus_stack(seat) - .last() - .and_then(|w| w.toplevel().get_surface().cloned()); - if let Some(keyboard) = seat.get_keyboard() { - keyboard.set_focus(surface.as_ref(), SERIAL_COUNTER.next_serial()); - ActiveFocus::set(seat, surface); - } + .get::() + .unwrap() + .active + .get(); + &mut self.spaces[active] } + WorkspaceMode::Global { active, .. } => &mut self.spaces[*active], + }; + self.workspace_state.update().remove_workspace_state(&workspace.handle, WState::Hidden); + self.toplevel_info_state.toplevel_enter_workspace(&window, &workspace.handle); + let focus_stack = workspace.focus_stack(&seat); + if layout::should_be_floating(&window) { + workspace.floating_layer.map_window(&mut workspace.space, window, &seat); + } else { + workspace.tiling_layer.map_window(&mut workspace.space, window, &seat, focus_stack.iter()); + } + } + + pub fn map_layer(&mut self, layer_surface: &LayerSurface, dh: &DisplayHandle) { + let pos = self.pending_layers.iter().position(|(l, _, _)| l == layer_surface).unwrap(); + let (layer_surface, output, seat) = self.pending_layers.remove(pos); + + let surface = layer_surface.wl_surface(); + let wants_focus = { + with_states(surface, |states| { + let state = states.cached_state.current::(); + matches!(state.layer, Layer::Top | Layer::Overlay) + && dbg!(state.keyboard_interactivity) != KeyboardInteractivity::None + }) + }; + + let mut map = layer_map_for_output(&output); + map.map_layer(dh, &layer_surface).unwrap(); + + if wants_focus { + self.set_focus(dh, Some(surface), &seat, None) + } + } + + pub fn move_current_window(&mut self, seat: &Seat, output: &Output, idx: usize) { + if idx > MAX_WORKSPACES { + return; } - self.shell.update_active(self.seats.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], + }; + if idx == workspace.idx as usize { + return; + } + + let maybe_window = workspace.focus_stack(seat).last(); + if let Some(window) = maybe_window { + 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); + + let new_workspace = &mut self.spaces[idx]; + 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); + } else { + new_workspace.tiling_layer.map_window(&mut new_workspace.space, window, &seat, focus_stack.iter()); + } + } } } + +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)); + 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 +) { + 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); + } + } +} \ No newline at end of file diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index a6936dac..9d2ec52c 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1,128 +1,47 @@ -use super::{layout, Layout}; -#[cfg(feature = "experimental")] -use crate::wayland::workspace as ext_work; -use indexmap::IndexSet; + +use crate::{ + state::State, + shell::layout::{tiling::TilingLayout, floating::FloatingLayout}, + wayland::protocols::workspace::WorkspaceHandle, +}; + use smithay::{ - desktop::{Kind, LayerSurface, Space, Window}, - reexports::wayland_protocols::xdg_shell::server::xdg_toplevel::{self, ResizeEdge}, + desktop::{Kind, Space, Window}, + reexports::{ + wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge}, + wayland_server::DisplayHandle, + }, wayland::{ output::Output, seat::{PointerGrabStartData, Seat}, Serial, }, + utils::IsAlive, }; -use std::{ - cell::{Ref, RefCell, RefMut}, - collections::HashMap, -}; - -pub struct FocusStack<'a>(Ref<'a, IndexSet>); -pub struct FocusStackMut<'a>(RefMut<'a, IndexSet>); - -impl<'a> FocusStack<'a> { - pub fn last(&self) -> Option { - self.0.iter().rev().find(|w| w.toplevel().alive()).cloned() - } - - pub fn iter<'b>(&'b self) -> Box + 'b> { - //working around object-safety constraints for trait Layout - Box::new(self.0.iter().rev().filter(|w| w.toplevel().alive())) - } -} - -impl<'a> FocusStackMut<'a> { - pub fn append(&mut self, window: &Window) { - self.0.retain(|w| w.toplevel().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 iter<'b>(&'b self) -> Box + 'b> { - //working around object-safety constraints for trait Layout - Box::new(self.0.iter().rev().filter(|w| w.toplevel().alive())) - } -} - -type FocusStackData = RefCell<(HashMap>, IndexSet)>; +use std::collections::HashMap; pub struct Workspace { pub(super) idx: u8, pub space: Space, - pub(super) layout: Box, - pub(super) pending_windows: Vec<(Window, Seat)>, - pub(super) pending_layers: Vec<(LayerSurface, Output, Seat)>, + pub tiling_layer: TilingLayout, + pub floating_layer: FloatingLayout, pub fullscreen: HashMap, - #[cfg(feature = "experimental")] - pub(super) ext_workspace: Option, + pub handle: WorkspaceHandle, } impl Workspace { - pub fn new(idx: u8) -> Workspace { + pub fn new(idx: u8, handle: WorkspaceHandle) -> Workspace { Workspace { idx, space: Space::new(None), - layout: layout::new_default_layout(), - pending_windows: Vec::new(), - pending_layers: Vec::new(), + tiling_layer: TilingLayout::new(), + floating_layer: FloatingLayout::new(), fullscreen: HashMap::new(), - #[cfg(feature = "experimental")] - ext_workspace: None, + handle, } } - pub fn focus_stack<'a>(&'a self, seat: &'a Seat) -> FocusStack<'a> { - seat.user_data() - .insert_if_missing(|| FocusStackData::new((HashMap::new(), IndexSet::new()))); - FocusStack(Ref::map( - seat.user_data().get::().unwrap().borrow(), - |map| map.0.get(&self.idx).unwrap_or(&map.1), //TODO: workaround until Ref::filter_map goes stable - )) - } - - pub fn focus_stack_mut<'a>(&'a self, seat: &'a Seat) -> FocusStackMut<'a> { - seat.user_data() - .insert_if_missing(|| FocusStackData::new((HashMap::new(), IndexSet::new()))); - FocusStackMut(RefMut::map( - seat.user_data() - .get::() - .unwrap() - .borrow_mut(), - |map| map.0.entry(self.idx).or_insert_with(|| IndexSet::new()), - )) - } - - pub fn pending_window(&mut self, window: Window, seat: &Seat) { - self.pending_windows.push((window, seat.clone())); - } - - pub fn pending_layer(&mut self, layer: LayerSurface, output: &Output, seat: &Seat) { - self.pending_layers - .push((layer, output.clone(), seat.clone())); - } - - pub(super) fn map_window(&mut self, window: &Window, seat: &Seat) { - seat.user_data() - .insert_if_missing(|| FocusStackData::new((HashMap::new(), IndexSet::new()))); - let focus_stack = FocusStackMut(RefMut::map( - seat.user_data() - .get::() - .unwrap() - .borrow_mut(), - |map| map.0.entry(self.idx).or_insert_with(|| IndexSet::new()), - )); - self.layout - .map_window(&mut self.space, window, seat, focus_stack.iter()) - } - - pub(super) fn unmap_window(&mut self, window: &Window) { - self.layout.unmap_window(&mut self.space, window) - } - - pub fn refresh(&mut self) { + pub fn refresh(&mut self, dh: &DisplayHandle) { let outputs = self.space.outputs().collect::>(); let dead_output_windows = self .fullscreen @@ -134,65 +53,40 @@ impl Workspace { for window in dead_output_windows { self.unfullscreen_request(&window); } - self.fullscreen.retain(|_, w| w.toplevel().alive()); - self.layout.refresh(&mut self.space); - self.space.refresh(); - } - - pub fn update_orientation(&mut self, seat: &Seat, orientation: layout::Orientation) { - seat.user_data() - .insert_if_missing(|| FocusStackData::new((HashMap::new(), IndexSet::new()))); - let focus_stack = FocusStackMut(RefMut::map( - seat.user_data() - .get::() - .unwrap() - .borrow_mut(), - |map| map.0.entry(self.idx).or_insert_with(|| IndexSet::new()), - )); - self.layout - .update_orientation(orientation, seat, &mut self.space, focus_stack.iter()) - } - - pub fn move_focus(&mut self, seat: &Seat, focus: layout::FocusDirection) -> Option { - seat.user_data() - .insert_if_missing(|| FocusStackData::new((HashMap::new(), IndexSet::new()))); - let focus_stack = FocusStackMut(RefMut::map( - seat.user_data() - .get::() - .unwrap() - .borrow_mut(), - |map| map.0.entry(self.idx).or_insert_with(|| IndexSet::new()), - )); - self.layout - .move_focus(focus, seat, &mut self.space, focus_stack.iter()) + self.fullscreen.retain(|_, w| w.alive()); + self.floating_layer.refresh(&mut self.space); + self.tiling_layer.refresh(&mut self.space); + self.space.refresh(dh); } pub fn maximize_request(&mut self, window: &Window, output: &Output) { if self.fullscreen.values().any(|w| w == window) { return; } - self.layout - .maximize_request(&mut self.space, window, output) + if self.floating_layer.windows.contains(window) { + self.floating_layer.maximize_request(&mut self.space, window, output); + } } pub fn move_request( &mut self, window: &Window, - seat: &Seat, + seat: &Seat, serial: Serial, start_data: PointerGrabStartData, ) { if self.fullscreen.values().any(|w| w == window) { return; } - self.layout - .move_request(&mut self.space, window, seat, serial, start_data) + if self.floating_layer.windows.contains(window) { + self.floating_layer.move_request(&mut self.space, window, seat, serial, start_data) + } } pub fn resize_request( &mut self, window: &Window, - seat: &Seat, + seat: &Seat, serial: Serial, start_data: PointerGrabStartData, edges: ResizeEdge, @@ -200,8 +94,12 @@ impl Workspace { if self.fullscreen.values().any(|w| w == window) { return; } - self.layout - .resize_request(&mut self.space, window, seat, serial, start_data, edges) + if self.floating_layer.windows.contains(window) { + self.floating_layer.resize_request(&mut self.space, window, seat, serial, start_data.clone(), edges) + } + if self.tiling_layer.windows.contains(window) { + self.tiling_layer.resize_request(&mut self.space, window, seat, serial, start_data, edges) + } } pub fn fullscreen_request(&mut self, window: &Window, output: &Output) { @@ -211,25 +109,21 @@ impl Workspace { #[allow(irrefutable_let_patterns)] if let Kind::Xdg(xdg) = &window.toplevel() { - if xdg.get_surface().is_some() { - let ret = xdg.with_pending_state(|state| { - state.states.set(xdg_toplevel::State::Fullscreen); - state.size = Some( - output - .current_mode() - .map(|m| m.size) - .unwrap_or((0, 0).into()) - .to_f64() - .to_logical(output.current_scale().fractional_scale()) - .to_i32_round(), - ); - }); + xdg.with_pending_state(|state| { + state.states.set(xdg_toplevel::State::Fullscreen); + state.size = Some( + output + .current_mode() + .map(|m| m.size) + .unwrap_or((0, 0).into()) + .to_f64() + .to_logical(output.current_scale().fractional_scale()) + .to_i32_round(), + ); + }); - if ret.is_ok() { - xdg.send_configure(); - self.fullscreen.insert(output.name(), window.clone()); - } - } + xdg.send_configure(); + self.fullscreen.insert(output.name(), window.clone()); } } @@ -237,16 +131,13 @@ impl Workspace { if self.fullscreen.values().any(|w| w == window) { #[allow(irrefutable_let_patterns)] if let Kind::Xdg(xdg) = &window.toplevel() { - if xdg.get_surface().is_some() { - let ret = xdg.with_pending_state(|state| { - state.states.unset(xdg_toplevel::State::Fullscreen); - state.size = None; - }); - if ret.is_ok() { - self.layout.refresh(&mut self.space); - xdg.send_configure(); - } - } + xdg.with_pending_state(|state| { + state.states.unset(xdg_toplevel::State::Fullscreen); + state.size = None; + }); + self.floating_layer.refresh(&mut self.space); + self.tiling_layer.refresh(&mut self.space); + xdg.send_configure(); } self.fullscreen.retain(|_, w| w != window); } @@ -266,6 +157,6 @@ impl Workspace { } self.fullscreen .get(&output.name()) - .filter(|w| w.toplevel().get_surface().is_some()) + .filter(|w| w.alive()) } }