From 647deb81f11576fec1540535419af65dd95d26f1 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 5 Apr 2024 13:53:35 +0200 Subject: [PATCH] shell: Move seats into shell --- src/backend/kms/mod.rs | 25 +- src/backend/mod.rs | 4 +- src/backend/render/mod.rs | 91 +++-- src/backend/winit.rs | 14 +- src/backend/x11.rs | 16 +- src/config/mod.rs | 52 +-- src/input/mod.rs | 335 ++++++----------- src/main.rs | 7 +- src/shell/element/mod.rs | 2 +- src/shell/element/window.rs | 2 +- src/shell/focus/mod.rs | 146 ++++---- src/shell/focus/target.rs | 2 +- src/shell/grabs/menu/default.rs | 50 ++- src/shell/grabs/menu/mod.rs | 7 +- src/shell/grabs/moving.rs | 2 +- src/shell/mod.rs | 359 +++++++------------ src/shell/seats.rs | 306 ++++++++++++++++ src/state.rs | 49 +-- src/utils/prelude.rs | 115 +----- src/wayland/handlers/compositor.rs | 16 +- src/wayland/handlers/fractional_scale.rs | 5 +- src/wayland/handlers/layer_shell.rs | 2 +- src/wayland/handlers/output_configuration.rs | 3 - src/wayland/handlers/screencopy/mod.rs | 8 +- src/wayland/handlers/screencopy/render.rs | 12 +- src/wayland/handlers/seat.rs | 2 +- src/wayland/handlers/toplevel_management.rs | 22 +- src/wayland/handlers/workspace.rs | 2 +- src/wayland/handlers/xdg_activation.rs | 4 +- src/wayland/handlers/xdg_shell/mod.rs | 10 +- src/xwayland.rs | 37 +- 31 files changed, 824 insertions(+), 883 deletions(-) create mode 100644 src/shell/seats.rs diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 680309c8..e512da1b 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -48,7 +48,6 @@ use smithay::{ udev::{all_gpus, primary_gpu, UdevBackend, UdevEvent}, }, desktop::utils::OutputPresentationFeedback, - input::Seat, output::{Mode as OutputMode, Output, OutputNoMode, PhysicalProperties, Subpixel}, reexports::{ calloop::{ @@ -324,12 +323,10 @@ pub fn init_backend( } } - let seats = state.common.seats().cloned().collect::>(); state.common.config.read_outputs( &mut state.common.output_configuration_state, &mut state.backend, &mut state.common.shell, - seats.into_iter(), &state.common.event_loop_handle, ); for surface in state @@ -701,12 +698,10 @@ impl State { self.common .output_configuration_state .add_heads(wl_outputs.iter()); - let seats = self.common.seats().cloned().collect::>(); self.common.config.read_outputs( &mut self.common.output_configuration_state, &mut self.backend, &mut self.common.shell, - seats.into_iter(), &self.common.event_loop_handle, ); @@ -836,12 +831,10 @@ impl State { self.common .output_configuration_state .add_heads(outputs_added.iter()); - let seats = self.common.seats().cloned().collect::>(); self.common.config.read_outputs( &mut self.common.output_configuration_state, &mut self.backend, &mut self.common.shell, - seats.iter().cloned(), &self.common.event_loop_handle, ); // Don't remove the outputs, before potentially new ones have been initialized. @@ -849,9 +842,7 @@ impl State { // If we have 0 outputs at some point, we won't quit, but shell doesn't know where to move // windows and workspaces to. for output in outputs_removed { - self.common - .shell - .remove_output(&output, seats.iter().cloned()); + self.common.shell.remove_output(&output); } { @@ -896,18 +887,14 @@ impl State { .output_configuration_state .remove_heads(outputs_removed.iter()); - let seats = self.common.seats().cloned().collect::>(); if self.backend.kms().session.is_active() { for output in outputs_removed { - self.common - .shell - .remove_output(&output, seats.iter().cloned()); + self.common.shell.remove_output(&output); } self.common.config.read_outputs( &mut self.common.output_configuration_state, &mut self.backend, &mut self.common.shell, - seats.into_iter(), &self.common.event_loop_handle, ); } else { @@ -1201,7 +1188,10 @@ impl Surface { let mut elements = workspace_elements( Some(&render_node), &mut renderer, - state, + &state.shell, + &state.config, + &state.theme, + state.clock.now(), &self.output, previous_workspace, workspace, @@ -1497,7 +1487,6 @@ impl KmsState { pub fn apply_config_for_output( &mut self, output: &Output, - seats: impl Iterator>, shell: &mut Shell, test_only: bool, loop_handle: &LoopHandle<'_, State>, @@ -1520,7 +1509,7 @@ impl KmsState { if !output_config.enabled { if !test_only { - shell.remove_output(output, seats); + shell.remove_output(output); if surface.surface.take().is_some() { // just drop it surface.pending = false; diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 59df16cb..688bb8ce 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -48,14 +48,14 @@ pub fn init_backend_auto( .outputs() .next() .with_context(|| "Backend initialized without output")?; - let initial_seat = crate::input::add_seat( + let initial_seat = crate::shell::create_seat( dh, &mut state.common.seat_state, output, &state.common.config, "seat-0".into(), ); - state.common.add_seat(initial_seat); + state.common.shell.seats.add_seat(initial_seat); } res } diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 6131ac11..7bae018a 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -12,14 +12,15 @@ use std::{ use crate::debug::fps_ui; use crate::{ backend::render::element::DamageElement, + config::Config, shell::{ focus::target::WindowGroup, grabs::{SeatMenuGrabState, SeatMoveGrabState}, layout::tiling::ANIMATION_DURATION, - CosmicMapped, CosmicMappedRenderElement, OverviewMode, SessionLock, Trigger, + CosmicMapped, CosmicMappedRenderElement, OverviewMode, SeatExt, SessionLock, Trigger, WorkspaceDelta, WorkspaceRenderElement, }, - state::{Common, Fps}, + state::Fps, utils::prelude::*, wayland::{ handlers::{ @@ -30,6 +31,7 @@ use crate::{ }, }; +use cosmic::Theme; use cosmic_comp_config::workspace::WorkspaceLayout; use keyframe::{ease, functions::EaseInOutCubic}; use smithay::{ @@ -56,7 +58,7 @@ use smithay::{ }, desktop::{layer_map_for_output, PopupManager}, output::{Output, OutputNoMode}, - utils::{IsAlive, Logical, Point, Rectangle, Scale, Transform}, + utils::{IsAlive, Logical, Monotonic, Point, Rectangle, Scale, Time, Transform}, wayland::{ dmabuf::get_dmabuf, shell::wlr_layer::Layer, @@ -387,7 +389,9 @@ pub enum CursorMode { #[profiling::function] pub fn cursor_elements<'frame, R>( renderer: &mut R, - state: &Common, + shell: &Shell, + theme: &Theme, + now: Time, output: &Output, mode: CursorMode, exclude_dnd_icon: bool, @@ -400,7 +404,7 @@ where let scale = output.current_scale().fractional_scale(); let mut elements = Vec::new(); - for seat in state.seats() { + for seat in shell.seats.iter() { let pointer = match seat.get_pointer() { Some(ptr) => ptr, None => continue, @@ -414,7 +418,7 @@ where seat, location, scale.into(), - state.clock.now(), + now, mode != CursorMode::NotDefault, ) .into_iter() @@ -438,7 +442,7 @@ where } } - let theme = state.theme.cosmic(); + let theme = theme.cosmic(); if let Some(grab_elements) = seat .user_data() .get::() @@ -469,7 +473,10 @@ where pub fn workspace_elements( _gpu: Option<&DrmNode>, renderer: &mut R, - state: &mut Common, + shell: &Shell, + config: &Config, + theme: &Theme, + now: Time, output: &Output, previous: Option<(WorkspaceHandle, usize, WorkspaceDelta)>, current: (WorkspaceHandle, usize), @@ -486,7 +493,9 @@ where { let mut elements = cursor_elements( renderer, - state, + shell, + theme, + now, output, cursor_mode, exclude_workspace_overview, @@ -500,7 +509,8 @@ where if let Some(fps) = _fps.as_mut() { let fps_overlay = fps_ui( _gpu, - state, + shell, + theme, renderer.glow_renderer_mut(), *fps, Rectangle::from_loc_and_size( @@ -516,7 +526,7 @@ where } // If session locked, only show session lock surfaces - if let Some(session_lock) = &state.shell.session_lock { + if let Some(session_lock) = &shell.session_lock { elements.extend( session_lock_elements(renderer, output, session_lock) .into_iter() @@ -525,15 +535,13 @@ where return Ok(elements); } - let theme = state.theme.cosmic(); - - let overview = state.shell.overview_mode(); - let (resize_mode, resize_indicator) = state.shell.resize_mode(); + let theme = theme.cosmic(); + let overview = shell.overview_mode(); + let (resize_mode, resize_indicator) = shell.resize_mode(); let resize_indicator = resize_indicator.map(|indicator| (resize_mode, indicator)); let swap_tree = if let OverviewMode::Started(Trigger::KeyboardSwap(_, desc), _) = &overview.0 { if current.0 != desc.handle { - state - .shell + shell .workspaces .space_for_handle(&desc.handle) .map(|w| w.tiling_layer.tree()) @@ -548,7 +556,7 @@ where overview.1.map(|indicator| (indicator, swap_tree)), ); - let last_active_seat = state.last_active_seat().clone(); + let last_active_seat = shell.seats.last_active(); let move_active = last_active_seat .user_data() .get::() @@ -559,12 +567,7 @@ where let output_size = output.geometry().size; let output_scale = output.current_scale().fractional_scale(); - let set = state - .shell - .workspaces - .sets - .get(output) - .ok_or(OutputNoMode)?; + let set = shell.workspaces.sets.get(output).ok_or(OutputNoMode)?; let workspace = set .workspaces .iter() @@ -593,7 +596,7 @@ where Vec::new() }; - let active_hint = if state.config.cosmic_conf.active_hint { + let active_hint = if config.cosmic_conf.active_hint { theme.active_hint as u8 } else { 0 @@ -603,8 +606,7 @@ where // they need to be over sticky windows, because they could be popups of sticky windows, // and we can't differenciate that. elements.extend( - state - .shell + shell .override_redirect_windows .iter() .filter(|or| { @@ -653,7 +655,7 @@ where }; let current_focus = (!move_active && is_active_space) - .then_some(&last_active_seat) + .then_some(last_active_seat) .map(|seat| workspace.focus_stack.get(seat)); let (w_elements, p_elements) = set.sticky_layer.render( @@ -683,10 +685,9 @@ where let offset = match previous.as_ref() { Some((previous, previous_idx, start)) => { - let layout = state.config.cosmic_conf.workspaces.workspace_layout; + let layout = config.cosmic_conf.workspaces.workspace_layout; - let workspace = state - .shell + let workspace = shell .workspaces .space_for_handle(&previous) .ok_or(OutputNoMode)?; @@ -724,7 +725,7 @@ where let (w_elements, p_elements) = workspace .render::( renderer, - (!move_active && is_active_space).then_some(&last_active_seat), + (!move_active && is_active_space).then_some(last_active_seat), overview.clone(), resize_indicator.clone(), active_hint, @@ -953,7 +954,10 @@ pub fn render_output<'d, R, Target, OffTarget>( target: Target, damage_tracker: &'d mut OutputDamageTracker, age: usize, - state: &mut Common, + shell: &Shell, + config: &Config, + theme: &Theme, + now: Time, output: &Output, cursor_mode: CursorMode, fps: Option<&mut Fps>, @@ -975,8 +979,8 @@ where WorkspaceRenderElement: RenderElement, Target: Clone, { - let (previous_workspace, workspace) = state.shell.workspaces.active(output); - let (previous_idx, idx) = state.shell.workspaces.active_num(output); + let (previous_workspace, workspace) = shell.workspaces.active(output); + let (previous_idx, idx) = shell.workspaces.active_num(output); let previous_workspace = previous_workspace .zip(previous_idx) .map(|((w, start), idx)| (w.handle, idx, start)); @@ -989,7 +993,10 @@ where damage_tracker, age, None, - state, + shell, + config, + theme, + now, output, previous_workspace, workspace, @@ -1081,7 +1088,7 @@ where }) }, )? { - frame.success(output.current_transform(), damage, state.clock.now()); + frame.success(output.current_transform(), damage, now); } } @@ -1099,7 +1106,10 @@ pub fn render_workspace<'d, R, Target, OffTarget>( damage_tracker: &'d mut OutputDamageTracker, age: usize, additional_damage: Option>>, - state: &mut Common, + shell: &Shell, + config: &Config, + theme: &Theme, + now: Time, output: &Output, previous: Option<(WorkspaceHandle, usize, WorkspaceDelta)>, current: (WorkspaceHandle, usize), @@ -1136,7 +1146,10 @@ where let mut elements: Vec> = workspace_elements( gpu, renderer, - state, + shell, + config, + theme, + now, output, previous, current, diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 2c546cb9..2746d4f8 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -3,7 +3,7 @@ use crate::{ backend::render, config::OutputConfig, - input::Devices, + shell::{Devices, SeatExt}, state::{BackendData, Common}, utils::prelude::*, }; @@ -63,7 +63,10 @@ impl WinitState { surface.clone(), &mut self.damage_tracker, age, - state, + &state.shell, + &state.config, + &state.theme, + state.clock.now(), &self.output, CursorMode::NotDefault, #[cfg(not(feature = "debug"))] @@ -198,8 +201,7 @@ pub fn init_backend( } PumpStatus::Exit(_) => { let output = state.backend.winit().output.clone(); - let seats = state.common.seats().cloned().collect::>(); - state.common.shell.remove_output(&output, seats.into_iter()); + state.common.shell.remove_output(&output); if let Some(token) = token.take() { event_loop_handle.remove(token); } @@ -225,12 +227,10 @@ pub fn init_backend( .output_configuration_state .add_heads(std::iter::once(&output)); state.common.shell.add_output(&output); - let seats = state.common.seats().cloned().collect::>(); state.common.config.read_outputs( &mut state.common.output_configuration_state, &mut state.backend, &mut state.common.shell, - seats.iter().cloned(), &state.common.event_loop_handle, ); state.launch_xwayland(None); @@ -299,7 +299,7 @@ impl State { // here we can handle special cases for winit inputs match event { WinitEvent::Focus(true) => { - for seat in self.common.seats().cloned().collect::>().iter() { + for seat in self.common.shell.seats.iter() { let devices = seat.user_data().get::().unwrap(); if devices.has_device(&WinitVirtualDevice) { seat.set_active_output(&self.backend.winit().output); diff --git a/src/backend/x11.rs b/src/backend/x11.rs index 5685e3ab..8c7eff3d 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -3,7 +3,7 @@ use crate::{ backend::render, config::OutputConfig, - input::Devices, + shell::{Devices, SeatExt}, state::{BackendData, Common}, utils::prelude::*, }; @@ -222,7 +222,10 @@ impl Surface { buffer.clone(), &mut self.damage_tracker, age as usize, - state, + &state.shell, + &state.config, + &state.theme, + state.clock.now(), &self.output, render::CursorMode::NotDefault, #[cfg(not(feature = "debug"))] @@ -362,12 +365,10 @@ pub fn init_backend( .output_configuration_state .add_heads(std::iter::once(&output)); state.common.shell.add_output(&output); - let seats = state.common.seats().cloned().collect::>(); state.common.config.read_outputs( &mut state.common.output_configuration_state, &mut state.backend, &mut state.common.shell, - seats.iter().cloned(), &state.common.event_loop_handle, ); state.launch_xwayland(None); @@ -394,10 +395,7 @@ pub fn init_backend( .surfaces .retain(|s| s.window.id() != window_id); for output in outputs_removed.into_iter() { - state - .common - .shell - .remove_output(&output, seats.iter().cloned()); + state.common.shell.remove_output(&output); } } X11Event::Resized { @@ -506,7 +504,7 @@ impl State { .unwrap(); let device = event.device(); - for seat in self.common.seats().cloned().collect::>().iter() { + for seat in self.common.shell.seats.iter() { let devices = seat.user_data().get::().unwrap(); if devices.has_device(&device) { seat.set_active_output(&output); diff --git a/src/config/mod.rs b/src/config/mod.rs index 9b9ec9bf..48c49931 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -7,7 +7,6 @@ use crate::{ }; use cosmic_config::{ConfigGet, CosmicConfigEntry}; use serde::{Deserialize, Serialize}; -use smithay::input::Seat; pub use smithay::{ backend::input::KeyState, input::keyboard::{keysyms as KeySyms, Keysym, ModifiersState}, @@ -277,10 +276,8 @@ impl Config { output_state: &mut OutputConfigurationState, backend: &mut BackendData, shell: &mut Shell, - seats: impl Iterator>, loop_handle: &LoopHandle<'_, State>, ) { - let seats = seats.collect::>(); let outputs = output_state.outputs().collect::>(); let mut infos = outputs .iter() @@ -311,13 +308,9 @@ impl Config { .get::>() .unwrap() .borrow_mut() = output_config; - if let Err(err) = backend.apply_config_for_output( - &output, - false, - shell, - seats.iter().cloned(), - loop_handle, - ) { + if let Err(err) = + backend.apply_config_for_output(&output, false, shell, loop_handle) + { warn!( ?err, "Failed to set new config for output {}.", @@ -346,13 +339,9 @@ impl Config { .get::>() .unwrap() .borrow_mut() = output_config; - if let Err(err) = backend.apply_config_for_output( - &output, - false, - shell, - seats.iter().cloned(), - loop_handle, - ) { + if let Err(err) = + backend.apply_config_for_output(&output, false, shell, loop_handle) + { error!(?err, "Failed to reset config for output {}.", output.name()); } else { if enabled { @@ -368,13 +357,9 @@ impl Config { self.write_outputs(output_state.outputs()); } else { for output in outputs { - if let Err(err) = backend.apply_config_for_output( - &output, - false, - shell, - seats.iter().cloned(), - loop_handle, - ) { + if let Err(err) = + backend.apply_config_for_output(&output, false, shell, loop_handle) + { warn!( ?err, "Failed to set new config for output {}.", @@ -533,7 +518,8 @@ fn config_changed(config: cosmic_config::Config, keys: Vec, state: &mut match key.as_str() { "xkb_config" => { let value = get_config::(&config, "xkb_config"); - for seat in state.common.seats().cloned().collect::>().iter() { + let seats = state.common.shell.seats.iter().cloned().collect::>(); + for seat in seats.into_iter() { if let Some(keyboard) = seat.get_keyboard() { if let Err(err) = keyboard.set_xkb_config(state, xkb_config_to_wl(&value)) { error!(?err, "Failed to load provided xkb config"); @@ -567,26 +553,14 @@ fn config_changed(config: cosmic_config::Config, keys: Vec, state: &mut let new = get_config::(&config, "autotile"); if new != state.common.config.cosmic_conf.autotile { state.common.config.cosmic_conf.autotile = new; - let seats: Vec<_> = state.common.seats().cloned().collect(); - let mut guard = state.common.shell.workspace_state.update(); - state - .common - .shell - .workspaces - .update_autotile(new, &mut guard, seats); + state.common.shell.update_autotile(new); } } "autotile_behavior" => { let new = get_config::(&config, "autotile_behavior"); if new != state.common.config.cosmic_conf.autotile_behavior { state.common.config.cosmic_conf.autotile_behavior = new; - let seats: Vec<_> = state.common.seats().cloned().collect(); - let mut guard = state.common.shell.workspace_state.update(); - state - .common - .shell - .workspaces - .update_autotile_behavior(new, &mut guard, seats); + state.common.shell.update_autotile_behavior(new); } } "active_hint" => { diff --git a/src/input/mod.rs b/src/input/mod.rs index 269cf568..57e5f9f6 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1,21 +1,20 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - backend::render::cursor::CursorState, - config::{xkb_config_to_wl, Action, Config, KeyModifiers, KeyPattern}, + config::{Action, KeyModifiers, KeyPattern}, input::gestures::{GestureState, SwipeAction}, shell::{ focus::{ target::{KeyboardFocusTarget, PointerFocusTarget}, FocusDirection, }, - grabs::{ReleaseMode, ResizeEdge, SeatMenuGrabState, SeatMoveGrabState}, + grabs::{ReleaseMode, ResizeEdge}, layout::{ floating::ResizeGrabMarker, tiling::{SwapWindowGrab, TilingLayout}, }, Direction, FocusResult, InvalidWorkspaceIndex, MoveResult, OverviewMode, ResizeDirection, - ResizeMode, Trigger, WorkspaceDelta, + ResizeMode, SeatExt, Trigger, WorkspaceDelta, }, state::Common, utils::prelude::*, @@ -40,20 +39,19 @@ use smithay::{ WindowSurfaceType, }, input::{ - keyboard::{FilterResult, KeysymHandle, LedState, XkbConfig}, + keyboard::{FilterResult, KeysymHandle}, pointer::{ - AxisFrame, ButtonEvent, CursorImageStatus, GestureHoldBeginEvent, GestureHoldEndEvent, + AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent, RelativeMotionEvent, }, touch::{DownEvent, MotionEvent as TouchMotionEvent, UpEvent}, - Seat, SeatState, + Seat, }, output::Output, reexports::{ - input::Device as InputDevice, - wayland_server::{protocol::wl_shm::Format as ShmFormat, DisplayHandle}, + input::Device as InputDevice, wayland_server::protocol::wl_shm::Format as ShmFormat, }, utils::{Point, Serial, SERIAL_COUNTER}, wayland::{ @@ -67,47 +65,23 @@ use smithay::{ }; #[cfg(not(feature = "debug"))] use tracing::info; -use tracing::{error, trace, warn}; +use tracing::{error, trace}; use xkbcommon::xkb::{Keycode, Keysym}; use std::{ any::Any, cell::RefCell, - collections::HashMap, os::unix::process::CommandExt, thread, time::{Duration, Instant}, }; -crate::utils::id_gen!(next_seat_id, SEAT_ID, SEAT_IDS); - pub mod gestures; -#[repr(transparent)] -pub struct SeatId(pub usize); -pub struct ActiveOutput(pub RefCell); #[derive(Default)] pub struct SupressedKeys(RefCell)>>); #[derive(Default, Debug)] pub struct ModifiersShortcutQueue(RefCell>); -#[derive(Default)] -pub struct Devices { - capabilities: RefCell>>, - // Used for updating keyboard leds on kms backend - keyboards: RefCell>, -} - -impl Default for SeatId { - fn default() -> SeatId { - SeatId(next_seat_id()) - } -} - -impl Drop for SeatId { - fn drop(&mut self) { - SEAT_IDS.lock().unwrap().remove(&self.0); - } -} impl SupressedKeys { fn add(&self, keysym: &KeysymHandle, token: impl Into>) { @@ -156,105 +130,6 @@ impl ModifiersShortcutQueue { } } -impl Devices { - fn add_device(&self, device: &D) -> Vec { - let id = device.id(); - let mut map = self.capabilities.borrow_mut(); - let caps = [ - DeviceCapability::Keyboard, - DeviceCapability::Pointer, - DeviceCapability::TabletTool, - ] - .iter() - .cloned() - .filter(|c| device.has_capability(*c)) - .collect::>(); - let new_caps = caps - .iter() - .cloned() - .filter(|c| map.values().flatten().all(|has| *c != *has)) - .collect::>(); - map.insert(id, caps); - - if device.has_capability(DeviceCapability::Keyboard) { - if let Some(device) = ::downcast_ref::(device) { - self.keyboards.borrow_mut().push(device.clone()); - } - } - - new_caps - } - - pub fn has_device(&self, device: &D) -> bool { - self.capabilities.borrow().contains_key(&device.id()) - } - - fn remove_device(&self, device: &D) -> Vec { - let id = device.id(); - - let mut keyboards = self.keyboards.borrow_mut(); - if let Some(idx) = keyboards.iter().position(|x| x.id() == id) { - keyboards.remove(idx); - } - - let mut map = self.capabilities.borrow_mut(); - map.remove(&id) - .unwrap_or(Vec::new()) - .into_iter() - .filter(|c| map.values().flatten().all(|has| *c != *has)) - .collect() - } - - pub fn update_led_state(&self, led_state: LedState) { - for keyboard in self.keyboards.borrow_mut().iter_mut() { - keyboard.led_update(led_state.into()); - } - } -} - -pub fn add_seat( - dh: &DisplayHandle, - seat_state: &mut SeatState, - output: &Output, - config: &Config, - name: String, -) -> Seat { - let mut seat = seat_state.new_wl_seat(dh, name); - let userdata = seat.user_data(); - userdata.insert_if_missing(SeatId::default); - userdata.insert_if_missing(Devices::default); - userdata.insert_if_missing(SupressedKeys::default); - userdata.insert_if_missing(ModifiersShortcutQueue::default); - userdata.insert_if_missing(SeatMoveGrabState::default); - userdata.insert_if_missing(SeatMenuGrabState::default); - userdata.insert_if_missing(CursorState::default); - userdata.insert_if_missing(|| ActiveOutput(RefCell::new(output.clone()))); - userdata.insert_if_missing(|| RefCell::new(CursorImageStatus::default_named())); - - // A lot of clients bind keyboard and pointer unconditionally once on launch.. - // Initial clients might race the compositor on adding periheral and - // end up in a state, where they are not able to receive input. - // Additionally a lot of clients don't handle keyboards/pointer objects being - // removed very well either and we don't want to crash applications, because the - // user is replugging their keyboard or mouse. - // - // So instead of doing the right thing (and initialize these capabilities as matching - // devices appear), we have to surrender to reality and just always expose a keyboard and pointer. - let conf = config.xkb_config(); - if let Err(err) = seat.add_keyboard(xkb_config_to_wl(&conf), 600, 25) { - warn!( - ?err, - "Failed to load provided xkb config. Trying default...", - ); - seat.add_keyboard(XkbConfig::default(), 600, 25) - .expect("Failed to load xkb configuration files"); - } - seat.add_pointer(); - seat.add_touch(); - - seat -} - impl State { pub fn process_input_event( &mut self, @@ -266,10 +141,8 @@ impl State { use smithay::backend::input::Event; match event { InputEvent::DeviceAdded { device } => { - let seat = &mut self.common.last_active_seat(); - let userdata = seat.user_data(); - let devices = userdata.get::().unwrap(); - for cap in devices.add_device(&device) { + let seat = &mut self.common.shell.seats.last_active(); + for cap in seat.devices().add_device(&device) { match cap { DeviceCapability::TabletTool => { seat.tablet_seat().add_tablet::( @@ -287,9 +160,8 @@ impl State { } } InputEvent::DeviceRemoved { device } => { - for seat in &mut self.common.seats() { - let userdata = seat.user_data(); - let devices = userdata.get::().unwrap(); + for seat in &mut self.common.shell.seats.iter() { + let devices = seat.devices(); if devices.has_device(&device) { for cap in devices.remove_device(&device) { match cap { @@ -314,7 +186,7 @@ impl State { let loop_handle = self.common.event_loop_handle.clone(); - if let Some(seat) = self.common.seat_with_device(&event.device()).cloned() { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { let userdata = seat.user_data(); let current_output = seat.active_output(); @@ -383,7 +255,7 @@ impl State { if let Some(focus) = TilingLayout::swap_trees(&mut old_workspace.tiling_layer, Some(&mut new_workspace.tiling_layer), &old_descriptor, &new_descriptor, &mut data.common.shell.toplevel_info_state) { let seat = seat.clone(); data.common.event_loop_handle.insert_idle(move |state| { - Common::set_focus(state, Some(&focus), &seat, None); + Shell::set_focus(state, Some(&focus), &seat, None); }); } old_workspace.refresh_focus_stack(); @@ -396,7 +268,7 @@ impl State { std::mem::drop(spaces); let seat = seat.clone(); data.common.event_loop_handle.insert_idle(move |state| { - Common::set_focus(state, Some(&focus), &seat, None); + Shell::set_focus(state, Some(&focus), &seat, None); }); } workspace.refresh_focus_stack(); @@ -414,7 +286,7 @@ impl State { if let Some(focus) = TilingLayout::move_tree(&mut old_workspace.tiling_layer, &mut new_workspace.tiling_layer, &new_workspace.handle, &seat, new_workspace.focus_stack.get(&seat).iter(), old_descriptor, &mut data.common.shell.toplevel_info_state) { let seat = seat.clone(); data.common.event_loop_handle.insert_idle(move |state| { - Common::set_focus(state, Some(&focus), &seat, None); + Shell::set_focus(state, Some(&focus), &seat, None); }); } old_workspace.refresh_focus_stack(); @@ -633,7 +505,7 @@ impl State { InputEvent::PointerMotion { event, .. } => { use smithay::backend::input::PointerMotionEvent; - if let Some(seat) = self.common.seat_with_device(&event.device()).cloned() { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { let current_output = seat.active_output(); let mut position = seat.get_pointer().unwrap().current_location().as_global(); @@ -831,7 +703,7 @@ impl State { } } InputEvent::PointerMotionAbsolute { event, .. } => { - if let Some(seat) = self.common.seat_with_device(&event.device()).cloned() { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { let output = seat.active_output(); let geometry = output.geometry(); let position = geometry.loc.to_f64() @@ -891,7 +763,7 @@ impl State { InputEvent::PointerButton { event, .. } => { use smithay::backend::input::{ButtonState, PointerButtonEvent}; - if let Some(seat) = self.common.seat_with_device(&event.device()).cloned() { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { #[cfg(feature = "debug")] if self.common.seats().position(|x| x == &seat).unwrap() == 0 && self.common.egui.active @@ -1033,7 +905,7 @@ impl State { } } } - Common::set_focus(self, under.as_ref(), &seat, Some(serial)); + Shell::set_focus(self, under.as_ref(), &seat, Some(serial)); } } else { if let OverviewMode::Started(Trigger::Pointer(action_button), _) = @@ -1067,7 +939,7 @@ impl State { 1.0 }; - if let Some(seat) = self.common.seat_with_device(&event.device()) { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { #[cfg(feature = "debug")] if self.common.seats().position(|x| x == seat).unwrap() == 0 && self.common.egui.active @@ -1121,7 +993,7 @@ impl State { } } InputEvent::GestureSwipeBegin { event, .. } => { - if let Some(seat) = self.common.seat_with_device(&event.device()) { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { if event.fingers() >= 3 { self.common.gesture_state = Some(GestureState::new(event.fingers())); } else { @@ -1139,7 +1011,7 @@ impl State { } } InputEvent::GestureSwipeUpdate { event, .. } => { - if let Some(seat) = self.common.seat_with_device(&event.device()).cloned() { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { let mut activate_action: Option = None; if let Some(ref mut gesture_state) = self.common.gesture_state { let first_update = gesture_state.update( @@ -1210,7 +1082,7 @@ impl State { } } InputEvent::GestureSwipeEnd { event, .. } => { - if let Some(seat) = self.common.seat_with_device(&event.device()).cloned() { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { if let Some(ref gesture_state) = self.common.gesture_state { match gesture_state.action { Some(SwipeAction::NextWorkspace) | Some(SwipeAction::PrevWorkspace) => { @@ -1246,7 +1118,7 @@ impl State { } } InputEvent::GesturePinchBegin { event, .. } => { - if let Some(seat) = self.common.seat_with_device(&event.device()) { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { let serial = SERIAL_COUNTER.next_serial(); let pointer = seat.get_pointer().unwrap(); pointer.gesture_pinch_begin( @@ -1260,7 +1132,7 @@ impl State { } } InputEvent::GesturePinchUpdate { event, .. } => { - if let Some(seat) = self.common.seat_with_device(&event.device()) { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { let pointer = seat.get_pointer().unwrap(); pointer.gesture_pinch_update( self, @@ -1274,7 +1146,7 @@ impl State { } } InputEvent::GesturePinchEnd { event, .. } => { - if let Some(seat) = self.common.seat_with_device(&event.device()) { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { let serial = SERIAL_COUNTER.next_serial(); let pointer = seat.get_pointer().unwrap(); pointer.gesture_pinch_end( @@ -1288,7 +1160,7 @@ impl State { } } InputEvent::GestureHoldBegin { event, .. } => { - if let Some(seat) = self.common.seat_with_device(&event.device()) { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { let serial = SERIAL_COUNTER.next_serial(); let pointer = seat.get_pointer().unwrap(); pointer.gesture_hold_begin( @@ -1302,7 +1174,7 @@ impl State { } } InputEvent::GestureHoldEnd { event, .. } => { - if let Some(seat) = self.common.seat_with_device(&event.device()) { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { let serial = SERIAL_COUNTER.next_serial(); let pointer = seat.get_pointer().unwrap(); pointer.gesture_hold_end( @@ -1316,7 +1188,7 @@ impl State { } } InputEvent::TouchDown { event, .. } => { - if let Some(seat) = self.common.seat_with_device(&event.device()).cloned() { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { let Some(output) = mapped_output_for_device(&self.common, &event.device()).cloned() else { @@ -1348,7 +1220,7 @@ impl State { } } InputEvent::TouchMotion { event, .. } => { - if let Some(seat) = self.common.seat_with_device(&event.device()).cloned() { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { let Some(output) = mapped_output_for_device(&self.common, &event.device()).cloned() else { @@ -1388,7 +1260,7 @@ impl State { } } - if let Some(seat) = self.common.seat_with_device(&event.device()) { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { let serial = SERIAL_COUNTER.next_serial(); let touch = seat.get_touch().unwrap(); touch.up( @@ -1402,19 +1274,19 @@ impl State { } } InputEvent::TouchCancel { event, .. } => { - if let Some(seat) = self.common.seat_with_device(&event.device()) { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { let touch = seat.get_touch().unwrap(); touch.cancel(self); } } InputEvent::TouchFrame { event, .. } => { - if let Some(seat) = self.common.seat_with_device(&event.device()) { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { let touch = seat.get_touch().unwrap(); touch.frame(self); } } InputEvent::TabletToolAxis { event, .. } => { - if let Some(seat) = self.common.seat_with_device(&event.device()).cloned() { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { let Some(output) = mapped_output_for_device(&self.common, &event.device()).cloned() else { @@ -1477,7 +1349,7 @@ impl State { } } InputEvent::TabletToolProximity { event, .. } => { - if let Some(seat) = self.common.seat_with_device(&event.device()).cloned() { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { let Some(output) = mapped_output_for_device(&self.common, &event.device()).cloned() else { @@ -1530,7 +1402,7 @@ impl State { } } InputEvent::TabletToolTip { event, .. } => { - if let Some(seat) = self.common.seat_with_device(&event.device()) { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { if let Some(tool) = seat.tablet_seat().get_tool(&event.tool()) { match event.tip_state() { TabletToolTipState::Down => { @@ -1544,7 +1416,7 @@ impl State { } } InputEvent::TabletToolButton { event, .. } => { - if let Some(seat) = self.common.seat_with_device(&event.device()) { + if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { if let Some(tool) = seat.tablet_seat().get_tool(&event.tool()) { tool.button( event.button(), @@ -1688,14 +1560,15 @@ impl State { Action::MoveToWorkspace(x) | Action::SendToWorkspace(x) => x - 1, _ => unreachable!(), }; - let _ = Shell::move_current_window( - self, + if let Ok(Some((target, _point))) = self.common.shell.move_current_window( seat, ¤t_output, (¤t_output, Some(workspace as usize)), follow, None, - ); + ) { + Shell::set_focus(self, Some(&target), seat, None); + } } x @ Action::MoveToLastWorkspace | x @ Action::SendToLastWorkspace => { let current_output = seat.active_output(); @@ -1705,14 +1578,15 @@ impl State { .workspaces .len(¤t_output) .saturating_sub(1); - let _ = Shell::move_current_window( - self, + if let Ok(Some((target, _point))) = self.common.shell.move_current_window( seat, ¤t_output, (¤t_output, Some(workspace as usize)), matches!(x, Action::MoveToLastWorkspace), None, - ); + ) { + Shell::set_focus(self, Some(&target), seat, None); + } } x @ Action::MoveToNextWorkspace | x @ Action::SendToNextWorkspace => { let current_output = seat.active_output(); @@ -1723,32 +1597,34 @@ impl State { .active_num(¤t_output) .1 .saturating_add(1); - if Shell::move_current_window( - self, + match self.common.shell.move_current_window( seat, ¤t_output, (¤t_output, Some(workspace as usize)), matches!(x, Action::MoveToNextWorkspace), direction, - ) - .is_err() - && propagate - { - if let Some(inferred) = pattern.inferred_direction() { - self.handle_action( - if matches!(x, Action::MoveToNextWorkspace) { - Action::MoveToOutput(inferred) - } else { - Action::SendToOutput(inferred) - }, - seat, - serial, - time, - pattern, - direction, - false, - ) + ) { + Ok(Some((target, _point))) => { + Shell::set_focus(self, Some(&target), seat, None); } + Err(_) if propagate => { + if let Some(inferred) = pattern.inferred_direction() { + self.handle_action( + if matches!(x, Action::MoveToNextWorkspace) { + Action::MoveToOutput(inferred) + } else { + Action::SendToOutput(inferred) + }, + seat, + serial, + time, + pattern, + direction, + false, + ) + } + } + _ => {} } } x @ Action::MoveToPreviousWorkspace | x @ Action::SendToPreviousWorkspace => { @@ -1761,32 +1637,34 @@ impl State { .1 .saturating_sub(1); // TODO: Possibly move to prev output, if idx < 0 - if Shell::move_current_window( - self, + match self.common.shell.move_current_window( seat, ¤t_output, (¤t_output, Some(workspace as usize)), matches!(x, Action::MoveToPreviousWorkspace), direction, - ) - .is_err() - && propagate - { - if let Some(inferred) = pattern.inferred_direction() { - self.handle_action( - if matches!(x, Action::MoveToPreviousWorkspace) { - Action::MoveToOutput(inferred) - } else { - Action::SendToOutput(inferred) - }, - seat, - serial, - time, - pattern, - direction, - false, - ) + ) { + Ok(Some((target, _point))) => { + Shell::set_focus(self, Some(&target), seat, None); } + Err(_) if propagate => { + if let Some(inferred) = pattern.inferred_direction() { + self.handle_action( + if matches!(x, Action::MoveToPreviousWorkspace) { + Action::MoveToOutput(inferred) + } else { + Action::SendToOutput(inferred) + }, + seat, + serial, + time, + pattern, + direction, + false, + ) + } + } + _ => {} } } Action::SwitchOutput(direction) => { @@ -1949,14 +1827,14 @@ impl State { .cloned(); if let Some(next_output) = next_output { - if let Ok(Some(new_pos)) = Shell::move_current_window( - self, + if let Ok(Some((target, new_pos))) = self.common.shell.move_current_window( seat, ¤t_output, (&next_output, None), is_move_action, Some(direction), ) { + Shell::set_focus(self, Some(&target), seat, None); if let Some(ptr) = seat.get_pointer() { ptr.motion( self, @@ -2011,14 +1889,14 @@ impl State { .next() .cloned(); if let Some(next_output) = next_output { - if let Ok(Some(new_pos)) = Shell::move_current_window( - self, + if let Ok(Some((target, new_pos))) = self.common.shell.move_current_window( seat, ¤t_output, (&next_output, None), matches!(x, Action::MoveToNextOutput), direction, ) { + Shell::set_focus(self, Some(&target), seat, None); if let Some(ptr) = seat.get_pointer() { ptr.motion( self, @@ -2046,14 +1924,14 @@ impl State { .next() .cloned(); if let Some(prev_output) = prev_output { - if let Ok(Some(new_pos)) = Shell::move_current_window( - self, + if let Ok(Some((target, new_pos))) = self.common.shell.move_current_window( seat, ¤t_output, (&prev_output, None), matches!(x, Action::MoveToPreviousOutput), direction, ) { + Shell::set_focus(self, Some(&target), seat, None); if let Some(ptr) = seat.get_pointer() { ptr.motion( self, @@ -2146,7 +2024,7 @@ impl State { } FocusResult::Handled => {} FocusResult::Some(target) => { - Common::set_focus(self, Some(&target), seat, None); + Shell::set_focus(self, Some(&target), seat, None); } } } @@ -2162,7 +2040,7 @@ impl State { true, ), MoveResult::ShiftFocus(shift) => { - Common::set_focus(self, Some(&shift), seat, None); + Shell::set_focus(self, Some(&shift), seat, None); } _ => { let current_output = seat.active_output(); @@ -2241,7 +2119,7 @@ impl State { } Action::ToggleStacking => { if let Some(new_focus) = self.common.shell.toggle_stacking_focused(seat) { - Common::set_focus(self, Some(&new_focus), seat, Some(serial)); + Shell::set_focus(self, Some(&new_focus), seat, Some(serial)); } } Action::ToggleTiling => { @@ -2251,13 +2129,9 @@ impl State { ) { let autotile = !self.common.config.cosmic_conf.autotile; self.common.config.cosmic_conf.autotile = autotile; - let seats: Vec<_> = self.common.seats().cloned().collect(); - let mut guard = self.common.shell.workspace_state.update(); - self.common.shell.workspaces.update_autotile( - self.common.config.cosmic_conf.autotile, - &mut guard, - seats, - ); + self.common + .shell + .update_autotile(self.common.config.cosmic_conf.autotile); let config = self.common.config.cosmic_helper.clone(); thread::spawn(move || { if let Err(err) = config.set("autotile", autotile) { @@ -2277,8 +2151,7 @@ impl State { workspace.toggle_floating_window_focused(seat); } Action::ToggleSticky => { - let seats = self.common.seats().cloned().collect::>(); - self.common.shell.toggle_sticky_current(seats.iter(), seat); + self.common.shell.toggle_sticky_current(seat); } Action::Spawn(command) => { let (token, data) = self @@ -2288,7 +2161,7 @@ impl State { .create_external_token(None); let (token, data) = (token.clone(), data.clone()); - let seat = self.common.last_active_seat(); + let seat = self.common.shell.seats.last_active(); let output = seat.active_output(); let workspace = self.common.shell.active_space_mut(&output); workspace.pending_tokens.insert(token.clone()); diff --git a/src/main.rs b/src/main.rs index 4dc55027..2d573109 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,8 +13,7 @@ use std::{env, ffi::OsString, os::unix::process::CommandExt, process, sync::Arc} use tracing::{error, info, warn}; use crate::{ - state::BackendData, utils::prelude::SeatExt, - wayland::handlers::compositor::client_compositor_state, + shell::SeatExt, state::BackendData, wayland::handlers::compositor::client_compositor_state, }; pub mod backend; @@ -181,7 +180,9 @@ fn init_wayland_display( .insert_source(source, |client_stream, _, state| { let node = match &state.backend { BackendData::Kms(kms_state) if kms_state.auto_assign => kms_state - .target_node_for_output(&state.common.last_active_seat().active_output()), + .target_node_for_output( + &state.common.shell.seats.last_active().active_output(), + ), _ => None, }; diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 4642a525..9e88be4d 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -72,7 +72,7 @@ use super::{ floating::{ResizeState, TiledCorners}, tiling::NodeDesc, }, - Direction, ManagedLayer, + Direction, ManagedLayer, SeatExt, }; space_elements! { diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 017ae7a9..e80105d1 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -416,7 +416,7 @@ impl Program for CosmicWindowInternal { if let Some(mapped) = state.common.shell.element_for_surface(&surface).cloned() { - let seat = state.common.last_active_seat().clone(); + let seat = state.common.shell.seats.last_active().clone(); state.common.shell.maximize_toggle(&mapped, &seat) } }); diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index c81d8b04..69749acb 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -20,7 +20,7 @@ use tracing::{debug, trace}; use self::target::{KeyboardFocusTarget, WindowGroup}; -use super::layout::floating::FloatingLayout; +use super::{layout::floating::FloatingLayout, SeatExt}; pub mod target; @@ -99,43 +99,10 @@ impl ActiveFocus { } impl Shell { - pub fn append_focus_stack(state: &mut State, mapped: &CosmicMapped, active_seat: &Seat) { - if mapped.is_minimized() { - return; - } - - // update FocusStack and notify layouts about new focus (if any window) - let workspace = state.common.shell.space_for_mut(&mapped); - let workspace = if workspace.is_none() { - state - .common - .shell - .active_space_mut(&active_seat.active_output()) - } else { - workspace.unwrap() - }; - - let mut focus_stack = workspace.focus_stack.get_mut(active_seat); - if Some(mapped) != focus_stack.last() { - trace!(?mapped, "Focusing window."); - focus_stack.append(&mapped); - // also remove popup grabs, if we are switching focus - if let Some(mut popup_grab) = active_seat - .user_data() - .get::() - .and_then(|x| x.take()) - { - if !popup_grab.has_ended() { - popup_grab.ungrab(PopupUngrabStrategy::All); - } - } - } - } - pub fn set_focus( state: &mut State, target: Option<&KeyboardFocusTarget>, - active_seat: &Seat, + seat: &Seat, serial: Option, ) { let element = match target { @@ -150,23 +117,57 @@ impl Shell { if mapped.is_minimized() { return; } - Self::append_focus_stack(state, &mapped, active_seat); + state.common.shell.append_focus_stack(&mapped, seat); } // update keyboard focus - if let Some(keyboard) = active_seat.get_keyboard() { - ActiveFocus::set(active_seat, target.cloned()); + if let Some(keyboard) = seat.get_keyboard() { + ActiveFocus::set(seat, target.cloned()); keyboard.set_focus( state, target.cloned(), serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()), ); } + + state.common.shell.update_active(); } - fn update_active<'a, 'b>(&mut self, seats: impl Iterator>) { + pub fn append_focus_stack(&mut self, mapped: &CosmicMapped, seat: &Seat) { + if mapped.is_minimized() { + return; + } + + // update FocusStack and notify layouts about new focus (if any window) + let workspace = self.space_for_mut(&mapped); + let workspace = if workspace.is_none() { + self.active_space_mut(&seat.active_output()) + } else { + workspace.unwrap() + }; + + let mut focus_stack = workspace.focus_stack.get_mut(seat); + if Some(mapped) != focus_stack.last() { + trace!(?mapped, "Focusing window."); + focus_stack.append(&mapped); + // 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(PopupUngrabStrategy::All); + } + } + } + } + + fn update_active<'a, 'b>(&mut self) { // update activate status - let focused_windows = seats + let focused_windows = self + .seats + .iter() .flat_map(|seat| { if matches!( seat.get_keyboard().unwrap().current_focus(), @@ -231,20 +232,16 @@ fn raise_with_children(floating_layer: &mut FloatingLayout, focused: &CosmicMapp } impl Common { - pub fn set_focus( - state: &mut State, - target: Option<&KeyboardFocusTarget>, - active_seat: &Seat, - serial: Option, - ) { - Shell::set_focus(state, target, active_seat, serial); - let seats = state.common.seats().cloned().collect::>(); - state.common.shell.update_active(seats.iter()); - } - pub fn refresh_focus(state: &mut State) { - let seats = state.common.seats().cloned().collect::>(); - for seat in seats { + for seat in state + .common + .shell + .seats + .iter() + .cloned() + .collect::>() + .into_iter() + { let output = seat.active_output(); if !state.common.shell.outputs().any(|o| o == &output) { seat.set_active_output(&state.common.shell.outputs().next().unwrap()); @@ -254,7 +251,7 @@ impl Common { if let Some(target) = last_known_focus { if target.alive() { - if focus_target_is_valid(state, &seat, &output, target) { + if focus_target_is_valid(&mut state.common.shell, &seat, &output, target) { continue; // Focus is valid } else { trace!("Wrong Window, focus fixup"); @@ -312,7 +309,7 @@ impl Common { } // update keyboard focus - let target = update_focus_target(state, &seat, &output); + let target = update_focus_target(&state.common.shell, &seat, &output); if let Some(keyboard) = seat.get_keyboard() { debug!("Restoring focus to {:?}", target.as_ref()); keyboard.set_focus(state, target.clone(), SERIAL_COUNTER.next_serial()); @@ -321,25 +318,24 @@ impl Common { } } - let seats = state.common.seats().cloned().collect::>(); - state.common.shell.update_active(seats.iter()) + state.common.shell.update_active() } } fn focus_target_is_valid( - state: &mut State, + shell: &mut Shell, seat: &Seat, output: &Output, target: KeyboardFocusTarget, ) -> bool { // If a session lock is active, only lock surfaces can be focused - if state.common.shell.session_lock.is_some() { + if shell.session_lock.is_some() { return matches!(target, KeyboardFocusTarget::LockSurface(_)); } // If an exclusive layer shell surface exists (on any output), only exclusive // shell surfaces can have focus, on the highest layer with exclusive surfaces. - if let Some(layer) = exclusive_layer_surface_layer(state) { + if let Some(layer) = exclusive_layer_surface_layer(shell) { return if let KeyboardFocusTarget::LayerSurface(layer_surface) = target { let data = layer_surface.cached_state(); (data.keyboard_interactivity, data.layer) == (KeyboardInteractivity::Exclusive, layer) @@ -350,9 +346,7 @@ fn focus_target_is_valid( match target { KeyboardFocusTarget::Element(mapped) => { - let is_sticky = state - .common - .shell + let is_sticky = shell .workspaces .sets .get(output) @@ -361,13 +355,13 @@ fn focus_target_is_valid( .mapped() .any(|m| m == &mapped); - let workspace = state.common.shell.active_space(&output); + let workspace = shell.active_space(&output); let focus_stack = workspace.focus_stack.get(&seat); let is_in_focus_stack = focus_stack.last().map(|m| m == &mapped).unwrap_or(false); let has_fullscreen = workspace.get_fullscreen().is_some(); if is_sticky && !is_in_focus_stack { - Shell::append_focus_stack(state, &mapped, seat); + shell.append_focus_stack(&mapped, seat); } (is_sticky || is_in_focus_stack) && !has_fullscreen @@ -375,16 +369,14 @@ fn focus_target_is_valid( KeyboardFocusTarget::LayerSurface(layer) => { layer_map_for_output(&output).layers().any(|l| l == &layer) } - KeyboardFocusTarget::Group(WindowGroup { node, .. }) => state - .common - .shell + KeyboardFocusTarget::Group(WindowGroup { node, .. }) => shell .workspaces .active(&output) .1 .tiling_layer .has_node(&node), KeyboardFocusTarget::Fullscreen(window) => { - let workspace = state.common.shell.active_space(&output); + let workspace = shell.active_space(&output); let focus_stack = workspace.focus_stack.get(&seat); focus_stack @@ -399,17 +391,17 @@ fn focus_target_is_valid( } fn update_focus_target( - state: &State, + shell: &Shell, seat: &Seat, output: &Output, ) -> Option { - if let Some(session_lock) = &state.common.shell.session_lock { + if let Some(session_lock) = &shell.session_lock { session_lock .surfaces .get(output) .cloned() .map(KeyboardFocusTarget::from) - } else if let Some(layer) = exclusive_layer_surface_layer(state) { + } else if let Some(layer) = exclusive_layer_surface_layer(shell) { layer_map_for_output(output) .layers() .find(|layer_surface| { @@ -419,12 +411,10 @@ fn update_focus_target( }) .cloned() .map(KeyboardFocusTarget::from) - } else if let Some(surface) = state.common.shell.active_space(&output).get_fullscreen() { + } else if let Some(surface) = shell.active_space(&output).get_fullscreen() { Some(KeyboardFocusTarget::Fullscreen(surface.clone())) } else { - state - .common - .shell + shell .active_space(&output) .focus_stack .get(&seat) @@ -436,9 +426,9 @@ fn update_focus_target( // Get the top-most layer, if any, with at least one surface with exclusive keyboard interactivity. // Only considers surface in `Top` or `Overlay` layer. -fn exclusive_layer_surface_layer(state: &State) -> Option { +fn exclusive_layer_surface_layer(shell: &Shell) -> Option { let mut layer = None; - for output in state.common.shell.outputs() { + for output in shell.outputs() { for layer_surface in layer_map_for_output(output).layers() { let data = layer_surface.cached_state(); if data.keyboard_interactivity == KeyboardInteractivity::Exclusive { diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index 85db9c97..6fc38ced 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -4,7 +4,7 @@ use crate::{ shell::{ element::{CosmicMapped, CosmicStack, CosmicWindow}, layout::tiling::ResizeForkTarget, - CosmicSurface, + CosmicSurface, SeatExt, }, utils::prelude::*, wayland::handlers::{screencopy::SessionHolder, xdg_shell::popup::get_popup_toplevel}, diff --git a/src/shell/grabs/menu/default.rs b/src/shell/grabs/menu/default.rs index 2e4e3928..9f49e7f5 100644 --- a/src/shell/grabs/menu/default.rs +++ b/src/shell/grabs/menu/default.rs @@ -8,21 +8,21 @@ use crate::{ grabs::ReleaseMode, CosmicSurface, Shell, }, - state::{Common, State}, + state::State, utils::{prelude::SeatExt, screenshot::screenshot_window}, }; use super::{Item, ResizeEdge}; fn toggle_stacking(state: &mut State, mapped: &CosmicMapped) { - let seat = state.common.last_active_seat().clone(); + let seat = state.common.shell.seats.last_active().clone(); if let Some(new_focus) = state.common.shell.toggle_stacking(mapped) { - Common::set_focus(state, Some(&new_focus), &seat, None); + Shell::set_focus(state, Some(&new_focus), &seat, None); } } fn move_prev_workspace(state: &mut State, mapped: &CosmicMapped) { - let seat = state.common.last_active_seat().clone(); + let seat = state.common.shell.seats.last_active().clone(); let (current_handle, output) = { let Some(ws) = state.common.shell.space_for(mapped) else { return; @@ -46,20 +46,21 @@ fn move_prev_workspace(state: &mut State, mapped: &CosmicMapped) { .map(|s| s.handle) }); if let Some(prev_handle) = maybe_handle { - Shell::move_window( - state, + if let Some((target, _)) = state.common.shell.move_window( Some(&seat), mapped, ¤t_handle, &prev_handle, true, None, - ); + ) { + Shell::set_focus(state, Some(&target), &seat, None); + } } } fn move_next_workspace(state: &mut State, mapped: &CosmicMapped) { - let seat = state.common.last_active_seat().clone(); + let seat = state.common.shell.seats.last_active().clone(); let (current_handle, output) = { let Some(ws) = state.common.shell.space_for(mapped) else { return; @@ -76,15 +77,16 @@ fn move_next_workspace(state: &mut State, mapped: &CosmicMapped) { .next() .map(|space| space.handle); if let Some(next_handle) = maybe_handle { - Shell::move_window( - state, + if let Some((target, _point)) = state.common.shell.move_window( Some(&seat), mapped, ¤t_handle, &next_handle, true, None, - ); + ) { + Shell::set_focus(state, Some(&target), &seat, None) + } } } @@ -112,7 +114,7 @@ pub fn tab_items( ) .into(); - let seat = state.common.last_active_seat().clone(); + let seat = state.common.shell.seats.last_active(); let output = seat.active_output(); let workspace = state.common.shell.workspaces.active_mut(&output); if is_tiled { @@ -125,7 +127,7 @@ pub fn tab_items( { workspace.unmaximize_request(&mapped); } - let focus_stack = workspace.focus_stack.get(&seat); + let focus_stack = workspace.focus_stack.get(seat); workspace .tiling_layer .map(mapped, Some(focus_stack.iter()), None); @@ -204,7 +206,7 @@ pub fn window_items( Item::new(fl!("window-menu-maximize"), move |handle| { let mapped = maximize_clone.clone(); let _ = handle.insert_idle(move |state| { - let seat = state.common.last_active_seat().clone(); + let seat = state.common.shell.seats.last_active().clone(); state.common.shell.maximize_toggle(&mapped, &seat); }); }) @@ -215,7 +217,7 @@ pub fn window_items( Item::new(fl!("window-menu-tiled"), move |handle| { let tile_clone = tile_clone.clone(); let _ = handle.insert_idle(move |state| { - let seat = state.common.last_active_seat().clone(); + let seat = state.common.shell.seats.last_active().clone(); if let Some(ws) = state.common.shell.space_for_mut(&tile_clone) { ws.toggle_floating_window(&seat, &tile_clone); } @@ -236,7 +238,7 @@ pub fn window_items( let move_clone = move_clone.clone(); let _ = handle.insert_idle(move |state| { if let Some(surface) = move_clone.wl_surface() { - let seat = state.common.last_active_seat().clone(); + let seat = state.common.shell.seats.last_active().clone(); Shell::move_request(state, &surface, &seat, None, ReleaseMode::Click, false); } }); @@ -247,7 +249,7 @@ pub fn window_items( Item::new(fl!("window-menu-resize-edge-top"), move |handle| { let resize_clone = resize_top_clone.clone(); let _ = handle.insert_idle(move |state| { - let seat = state.common.last_active_seat().clone(); + let seat = state.common.shell.seats.last_active().clone(); Shell::menu_resize_request(state, &resize_clone, &seat, ResizeEdge::TOP); }); }) @@ -255,7 +257,7 @@ pub fn window_items( Item::new(fl!("window-menu-resize-edge-left"), move |handle| { let resize_clone = resize_left_clone.clone(); let _ = handle.insert_idle(move |state| { - let seat = state.common.last_active_seat().clone(); + let seat = state.common.shell.seats.last_active().clone(); Shell::menu_resize_request(state, &resize_clone, &seat, ResizeEdge::LEFT); }); }) @@ -263,7 +265,7 @@ pub fn window_items( Item::new(fl!("window-menu-resize-edge-right"), move |handle| { let resize_clone = resize_right_clone.clone(); let _ = handle.insert_idle(move |state| { - let seat = state.common.last_active_seat().clone(); + let seat = state.common.shell.seats.last_active().clone(); Shell::menu_resize_request(state, &resize_clone, &seat, ResizeEdge::RIGHT); }); }) @@ -271,7 +273,7 @@ pub fn window_items( Item::new(fl!("window-menu-resize-edge-bottom"), move |handle| { let resize_clone = resize_bottom_clone.clone(); let _ = handle.insert_idle(move |state| { - let seat = state.common.last_active_seat().clone(); + let seat = state.common.shell.seats.last_active().clone(); Shell::menu_resize_request(state, &resize_clone, &seat, ResizeEdge::BOTTOM); }); }) @@ -299,12 +301,8 @@ pub fn window_items( Item::new(fl!("window-menu-sticky"), move |handle| { let mapped = sticky_clone.clone(); let _ = handle.insert_idle(move |state| { - let seat = state.common.last_active_seat().clone(); - let seats = state.common.seats().cloned().collect::>(); - state - .common - .shell - .toggle_sticky(seats.iter(), &seat, &mapped); + let seat = state.common.shell.seats.last_active().clone(); + state.common.shell.toggle_sticky(&seat, &mapped); }); }) .toggled(is_sticky), diff --git a/src/shell/grabs/menu/mod.rs b/src/shell/grabs/menu/mod.rs index 7426830a..5b887336 100644 --- a/src/shell/grabs/menu/mod.rs +++ b/src/shell/grabs/menu/mod.rs @@ -40,10 +40,11 @@ use smithay::{ use crate::{ shell::focus::target::PointerFocusTarget, + shell::SeatExt, state::State, utils::{ iced::{IcedElement, Program}, - prelude::{Global, OutputExt, PointGlobalExt, PointLocalExt, SeatExt, SizeExt}, + prelude::{Global, OutputExt, PointGlobalExt, PointLocalExt, SizeExt}, }, }; @@ -215,7 +216,7 @@ impl Program for ContextMenu { if let Some(Item::Submenu { items, .. }) = self.items.get_mut(idx) { let items = items.clone(); let _ = loop_handle.insert_idle(move |state| { - let seat = state.common.last_active_seat(); + let seat = state.common.shell.seats.last_active(); let grab_state = seat .user_data() .get::() @@ -300,7 +301,7 @@ impl Program for ContextMenu { Message::ItemLeft(idx, _) => { if let Some(Item::Submenu { .. }) = self.items.get_mut(idx) { let _ = loop_handle.insert_idle(|state| { - let seat = state.common.last_active_seat(); + let seat = state.common.shell.seats.last_active(); let grab_state = seat .user_data() .get::() diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index 220b12fc..5c0e884c 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -846,7 +846,7 @@ impl Drop for MoveGrab { ); } } - Common::set_focus( + Shell::set_focus( state, Some(&KeyboardFocusTarget::from(mapped)), &seat, diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 481d2b0f..1d76ff18 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1,4 +1,5 @@ use calloop::LoopHandle; +use cosmic::Theme; use indexmap::IndexMap; use std::{ collections::HashMap, @@ -72,8 +73,10 @@ pub mod element; pub mod focus; pub mod grabs; pub mod layout; +mod seats; mod workspace; pub use self::element::{CosmicMapped, CosmicMappedRenderElement, CosmicSurface}; +pub use self::seats::*; pub use self::workspace::*; use self::{ element::{ @@ -199,6 +202,7 @@ pub struct Shell { pub pending_activations: HashMap, pub override_redirect_windows: Vec, pub session_lock: Option, + pub seats: Seats, // wayland_state pub layer_shell_state: WlrLayerShellState, @@ -650,10 +654,10 @@ impl Workspaces { } } - pub fn remove_output( + pub fn remove_output<'a>( &mut self, output: &Output, - seats: impl Iterator>, + seats: impl Iterator>, workspace_state: &mut WorkspaceUpdateGuard<'_, State>, toplevel_info_state: &mut ToplevelInfoState, xdg_activation_state: &XdgActivationState, @@ -1024,21 +1028,22 @@ impl Workspaces { } } - pub fn update_autotile_behavior( + fn update_autotile_behavior<'a>( &mut self, behavior: TileBehavior, guard: &mut WorkspaceUpdateGuard<'_, State>, - seats: Vec>, + seats: impl Iterator>, ) { self.autotile_behavior = behavior; self.apply_tile_change(guard, seats); } - fn apply_tile_change( + fn apply_tile_change<'a>( &mut self, guard: &mut WorkspaceUpdateGuard<'_, State>, - seats: Vec>, + seats: impl Iterator>, ) { + let seats = seats.cloned().collect::>(); for (_, set) in &mut self.sets { set.tiling_enabled = self.autotile; @@ -1056,11 +1061,11 @@ impl Workspaces { } } - pub fn update_autotile( + fn update_autotile<'a>( &mut self, autotile: bool, guard: &mut WorkspaceUpdateGuard<'_, State>, - seats: Vec>, + seats: impl Iterator>, ) { self.autotile = autotile; self.apply_tile_change(guard, seats); @@ -1105,6 +1110,7 @@ impl Shell { Shell { popups: PopupManager::default(), workspaces: Workspaces::new(config, theme.clone()), + seats: Seats::new(), pending_windows: Vec::new(), pending_layers: Vec::new(), @@ -1139,10 +1145,10 @@ impl Shell { self.refresh(); // fixes indicies of any moved workspaces } - pub fn remove_output(&mut self, output: &Output, seats: impl Iterator>) { + pub fn remove_output(&mut self, output: &Output) { self.workspaces.remove_output( output, - seats, + self.seats.iter(), &mut self.workspace_state.update(), &mut self.toplevel_info_state, &self.xdg_activation_state, @@ -1601,11 +1607,10 @@ impl Shell { } } - pub fn overview_mode(&mut self) -> (OverviewMode, Option) { + pub fn overview_mode(&self) -> (OverviewMode, Option) { if let OverviewMode::Ended(_, timestamp) = self.overview_mode { if Instant::now().duration_since(timestamp) > ANIMATION_DURATION { - self.overview_mode = OverviewMode::None; - self.swap_indicator = None; + return (OverviewMode::None, None); } } @@ -1641,11 +1646,10 @@ impl Shell { } } - pub fn resize_mode(&mut self) -> (ResizeMode, Option) { + pub fn resize_mode(&self) -> (ResizeMode, Option) { if let ResizeMode::Ended(timestamp, _) = self.resize_mode { if Instant::now().duration_since(timestamp) > ANIMATION_DURATION { - self.resize_mode = ResizeMode::None; - self.resize_indicator = None; + return (ResizeMode::None, None); } } @@ -1673,6 +1677,19 @@ impl Shell { #[profiling::function] pub fn refresh(&mut self) { + if let OverviewMode::Ended(_, timestamp) = self.overview_mode { + if Instant::now().duration_since(timestamp) > ANIMATION_DURATION { + self.overview_mode = OverviewMode::None; + self.swap_indicator = None; + } + } + if let ResizeMode::Ended(timestamp, _) = self.resize_mode { + if Instant::now().duration_since(timestamp) > ANIMATION_DURATION { + self.resize_mode = ResizeMode::None; + self.resize_indicator = None; + } + } + self.popups.cleanup(); self.xdg_activation_state.retain_tokens(|_, data| { @@ -1764,23 +1781,24 @@ impl Shell { }; } - pub fn map_window(state: &mut State, window: &CosmicSurface) { - let pos = state - .common - .shell + #[must_use] + pub fn map_window( + &mut self, + window: &CosmicSurface, + evlh: &LoopHandle<'static, State>, + theme: &Theme, + ) -> Option { + let pos = self .pending_windows .iter() .position(|(w, _, _)| w == window) .unwrap(); - let (window, seat, output) = state.common.shell.pending_windows.remove(pos); + let (window, seat, output) = self.pending_windows.remove(pos); let parent_is_sticky = if let Some(toplevel) = window.0.toplevel() { if let Some(parent) = toplevel.parent() { - if let Some(elem) = state.common.shell.element_for_surface(&parent) { - state - .common - .shell - .workspaces + if let Some(elem) = self.element_for_surface(&parent) { + self.workspaces .sets .values() .any(|set| set.sticky_layer.mapped().any(|m| m == elem)) @@ -1794,11 +1812,7 @@ impl Shell { false }; - let pending_activation = state - .common - .shell - .pending_activations - .remove(&(&window).into()); + let pending_activation = self.pending_activations.remove(&(&window).into()); let workspace_handle = match pending_activation { Some(ActivationContext::Workspace(handle)) => Some(handle), _ => None, @@ -1809,22 +1823,16 @@ impl Shell { // this is beyond stupid, just to make the borrow checker happy let workspace = if let Some(handle) = workspace_handle.filter(|handle| { - state - .common - .shell - .workspaces + self.workspaces .spaces() .any(|space| &space.handle == handle) }) { - state - .common - .shell - .workspaces + self.workspaces .spaces_mut() .find(|space| space.handle == handle) .unwrap() } else { - state.common.shell.workspaces.active_mut(&output) + self.workspaces.active_mut(&output) }; if output != workspace.output { output = workspace.output.clone(); @@ -1832,57 +1840,34 @@ impl Shell { if let Some((mapped, layer, previous_workspace)) = workspace.remove_fullscreen() { let old_handle = workspace.handle.clone(); - let new_workspace_handle = state - .common - .shell + let new_workspace_handle = self .workspaces .space_for_handle(&previous_workspace) .is_some() .then_some(previous_workspace) .unwrap_or(old_handle); - state.common.shell.remap_unfullscreened_window( - mapped, - &old_handle, - &new_workspace_handle, - layer, - ); + self.remap_unfullscreened_window(mapped, &old_handle, &new_workspace_handle, layer); }; - let active_handle = state.common.shell.workspaces.active(&output).1.handle; + let active_handle = self.active_space(&output).handle; let workspace = if let Some(handle) = workspace_handle.filter(|handle| { - state - .common - .shell - .workspaces + self.workspaces .spaces() .any(|space| &space.handle == handle) }) { - state - .common - .shell - .workspaces + self.workspaces .spaces_mut() .find(|space| space.handle == handle) .unwrap() } else { - state.common.shell.workspaces.active_mut(&output) + self.workspaces.active_mut(&output) }; - state - .common - .shell - .toplevel_info_state - .new_toplevel(&window, &state.common.shell.workspace_state); - state - .common - .shell - .toplevel_info_state + self.toplevel_info_state.new_toplevel(&window, &self.workspace_state); + self.toplevel_info_state .toplevel_enter_output(&window, &output); - state - .common - .shell - .toplevel_info_state + self.toplevel_info_state .toplevel_enter_workspace(&window, &workspace.handle); let workspace_output = workspace.output.clone(); @@ -1899,16 +1884,16 @@ impl Shell { { focused.stack_ref().unwrap().add_window(window, None); if was_activated { - state.common.shell.set_urgent(&workspace_handle); + self.set_urgent(&workspace_handle); } - return; + return None; } } let mapped = CosmicMapped::from(CosmicWindow::new( window.clone(), - state.common.event_loop_handle.clone(), - state.common.theme.clone(), + evlh.clone(), + theme.clone(), )); #[cfg(feature = "debug")] { @@ -1935,9 +1920,7 @@ impl Shell { } if !parent_is_sticky && should_be_fullscreen { - let from = state - .common - .shell + let from = self .toplevel_management_state .minimize_rectangle(&output, &mapped.active_window()); @@ -1945,37 +1928,34 @@ impl Shell { } if parent_is_sticky { - let seats = state.common.seats().cloned().collect::>(); - state - .common - .shell - .toggle_sticky(seats.iter(), &seat, &mapped); + self.toggle_sticky(&seat, &mapped); } - if (workspace_output == seat.active_output() && active_handle == workspace_handle) + let new_target = if (workspace_output == seat.active_output() + && active_handle == workspace_handle) || parent_is_sticky { // TODO: enforce focus stealing prevention by also checking the same rules as for the else case. - Shell::set_focus( - state, - Some(&KeyboardFocusTarget::from(mapped.clone())), - &seat, - None, - ); - } else if workspace_empty || was_activated || should_be_fullscreen { - Shell::append_focus_stack(state, &mapped, &seat); - state.common.shell.set_urgent(&workspace_handle); + Some(KeyboardFocusTarget::from(mapped.clone())) + } else { + if workspace_empty || was_activated || should_be_fullscreen { + self.append_focus_stack(&mapped, &seat); + self.set_urgent(&workspace_handle); + } + None + }; + + let active_space = self.active_space(&output); + for mapped in active_space.mapped() { + self.update_reactive_popups(mapped); } - let active_space = state.common.shell.active_space(&output); - for mapped in active_space.mapped() { - state.common.shell.update_reactive_popups(mapped); - } + new_target } - pub fn map_override_redirect(state: &mut State, window: X11Surface) { + pub fn map_override_redirect(&mut self, window: X11Surface) { let geo = window.geometry(); - for (output, overlap) in state.common.shell.outputs().cloned().filter_map(|o| { + for (output, overlap) in self.outputs().cloned().filter_map(|o| { o.geometry() .as_logical() .intersection(geo) @@ -1984,18 +1964,17 @@ impl Shell { window.output_enter(&output, overlap); } - state.common.shell.override_redirect_windows.push(window); + self.override_redirect_windows.push(window); } - pub fn map_layer(state: &mut State, layer_surface: &LayerSurface) { - let pos = state - .common - .shell + #[must_use] + pub fn map_layer(&mut self, layer_surface: &LayerSurface) -> Option { + let pos = self .pending_layers .iter() .position(|(l, _, _)| l == layer_surface) .unwrap(); - let (layer_surface, output, seat) = state.common.shell.pending_layers.remove(pos); + let (layer_surface, output, _seat) = self.pending_layers.remove(pos); let wants_focus = { with_states(layer_surface.wl_surface(), |states| { @@ -2009,13 +1988,11 @@ impl Shell { let mut map = layer_map_for_output(&output); map.map_layer(&layer_surface).unwrap(); } - for workspace in state.common.shell.workspaces.spaces_mut() { + for workspace in self.workspaces.spaces_mut() { workspace.tiling_layer.recalculate(); } - if wants_focus { - Shell::set_focus(state, Some(&layer_surface.into()), &seat, None) - } + wants_focus.then(|| layer_surface.into()) } pub fn unmap_surface(&mut self, surface: &S, seat: &Seat) @@ -2101,83 +2078,50 @@ impl Shell { }) } + #[must_use] pub fn move_window( - state: &mut State, + &mut self, seat: Option<&Seat>, mapped: &CosmicMapped, from: &WorkspaceHandle, to: &WorkspaceHandle, follow: bool, direction: Option, - ) -> Option> { - let from_output = state - .common - .shell - .workspaces - .space_for_handle(from)? - .output - .clone(); - let to_output = state - .common - .shell - .workspaces - .space_for_handle(to)? - .output - .clone(); + ) -> Option<(KeyboardFocusTarget, Point)> { + let from_output = self.workspaces.space_for_handle(from)?.output.clone(); + let to_output = self.workspaces.space_for_handle(to)?.output.clone(); - let from_workspace = state - .common - .shell - .workspaces - .space_for_handle_mut(from) - .unwrap(); // checked above + let from_workspace = self.workspaces.space_for_handle_mut(from).unwrap(); // checked above let window_state = from_workspace.unmap(mapped)?; let elements = from_workspace.mapped().cloned().collect::>(); for (toplevel, _) in mapped.windows() { - state - .common - .shell - .toplevel_info_state + self.toplevel_info_state .toplevel_leave_workspace(&toplevel, from); if from_output != to_output { - state - .common - .shell - .toplevel_info_state + self.toplevel_info_state .toplevel_leave_output(&toplevel, &from_output); } } for mapped in elements.into_iter() { - state.common.shell.update_reactive_popups(&mapped); + self.update_reactive_popups(&mapped); } let new_pos = if follow { if let Some(seat) = seat { seat.set_active_output(&to_output); } - state - .common - .shell - .workspaces + self.workspaces .idx_for_handle(&to_output, to) .and_then(|to_idx| { - state - .common - .shell - .activate(&to_output, to_idx, WorkspaceDelta::new_shortcut()) + self.activate(&to_output, to_idx, WorkspaceDelta::new_shortcut()) .unwrap() }) } else { None }; - let any_seat = seat.unwrap_or(state.common.last_active_seat()).clone(); - let mut to_workspace = state - .common - .shell - .workspaces - .space_for_handle_mut(to) - .unwrap(); // checked above + let any_seat = seat.unwrap_or(self.seats.last_active()).clone(); + let mut to_workspace = self.workspaces.space_for_handle_mut(to).unwrap(); // checked above let focus_stack = seat.map(|seat| to_workspace.focus_stack.get(&seat)); if window_state.layer == ManagedLayer::Floating || !to_workspace.tiling_enabled { to_workspace.floating_layer.map(mapped.clone(), None); @@ -2194,34 +2138,25 @@ impl Shell { if let Some((mapped, layer, previous_workspace)) = to_workspace.remove_fullscreen() { let old_handle = to.clone(); - let new_workspace_handle = state - .common - .shell + let new_workspace_handle = self .workspaces .space_for_handle(&previous_workspace) .is_some() .then_some(previous_workspace) .unwrap_or(old_handle); - state.common.shell.remap_unfullscreened_window( + self.remap_unfullscreened_window( mapped, &old_handle, &new_workspace_handle, layer, ); - to_workspace = state - .common - .shell - .workspaces - .space_for_handle_mut(to) - .unwrap(); + to_workspace = self.workspaces.space_for_handle_mut(to).unwrap(); // checked above } } - let from = state - .common - .shell + let from = self .toplevel_management_state .minimize_rectangle(&to_output, &mapped.active_window()); @@ -2241,58 +2176,43 @@ impl Shell { .collect::>() .into_iter() { - state.common.shell.update_reactive_popups(&mapped); + self.update_reactive_popups(&mapped); } for (toplevel, _) in mapped.windows() { if from_output != to_output { - state - .common - .shell - .toplevel_info_state + self.toplevel_info_state .toplevel_enter_output(&toplevel, &to_output); } - state - .common - .shell - .toplevel_info_state + self.toplevel_info_state .toplevel_enter_workspace(&toplevel, to); } - if follow { - if let Some(seat) = seat { - Common::set_focus(state, Some(&focus_target), &seat, None); - } - } - - new_pos + new_pos.map(|pos| (focus_target, pos)) } + #[must_use] pub fn move_current_window( - state: &mut State, + &mut self, seat: &Seat, from_output: &Output, to: (&Output, Option), follow: bool, direction: Option, - ) -> Result>, InvalidWorkspaceIndex> { + ) -> Result)>, InvalidWorkspaceIndex> { let (to_output, to_idx) = to; - let to_idx = to_idx.unwrap_or(state.common.shell.workspaces.active_num(to_output).1); + let to_idx = to_idx.unwrap_or(self.workspaces.active_num(to_output).1); - if from_output == to_output - && to_idx == state.common.shell.workspaces.active_num(from_output).1 - { + if from_output == to_output && to_idx == self.workspaces.active_num(from_output).1 { return Ok(None); } - let to = state - .common - .shell + let to = self .workspaces .get(to_idx, to_output) .map(|ws| ws.handle) .ok_or(InvalidWorkspaceIndex)?; - let from_workspace = state.common.shell.workspaces.active_mut(from_output); + let from_workspace = self.workspaces.active_mut(from_output); let maybe_window = from_workspace.focus_stack.get(seat).last().cloned(); let Some(mapped) = maybe_window else { @@ -2301,15 +2221,7 @@ impl Shell { let from = from_workspace.handle; - Ok(Shell::move_window( - state, - Some(seat), - &mapped, - &from, - &to, - follow, - direction, - )) + Ok(self.move_window(Some(seat), &mapped, &from, &to, follow, direction)) } pub fn update_reactive_popups(&self, mapped: &CosmicMapped) { @@ -2467,9 +2379,8 @@ impl Shell { return; } - let seats = state.common.seats().cloned().collect::>(); for workspace in state.common.shell.workspaces.spaces_mut() { - for seat in seats.iter() { + for seat in state.common.shell.seats.iter() { let mut stack = workspace.focus_stack.get_mut(seat); stack.remove(&old_mapped); } @@ -2673,6 +2584,7 @@ impl Shell { } } + #[must_use] pub fn next_focus<'a>(&mut self, direction: FocusDirection, seat: &Seat) -> FocusResult { let overview = self.overview_mode().0; let output = seat.active_output(); @@ -2804,6 +2716,7 @@ impl Shell { } } + #[must_use] pub fn move_current_element<'a>( &mut self, direction: Direction, @@ -3270,6 +3183,7 @@ impl Shell { } } + #[must_use] pub fn toggle_stacking(&mut self, window: &CosmicMapped) -> Option { if let Some(set) = self .workspaces @@ -3291,6 +3205,7 @@ impl Shell { } } + #[must_use] pub fn toggle_stacking_focused(&mut self, seat: &Seat) -> Option { let set = self.workspaces.sets.get_mut(&seat.active_output()).unwrap(); let workspace = &mut set.workspaces[set.active]; @@ -3315,16 +3230,10 @@ impl Shell { } } - pub fn toggle_sticky<'a>( - &mut self, - seats: impl Iterator>, - seat: &Seat, - mapped: &CosmicMapped, - ) { + pub fn toggle_sticky<'a>(&mut self, seat: &Seat, mapped: &CosmicMapped) { // clean from focus-stacks - let seats = seats.collect::>(); for workspace in self.workspaces.spaces_mut() { - for seat in seats.iter() { + for seat in self.seats.iter() { let mut stack = workspace.focus_stack.get_mut(seat); stack.remove(mapped); } @@ -3393,18 +3302,16 @@ impl Shell { _ => workspace.floating_layer.map(mapped.clone(), geometry.loc), } } + + self.append_focus_stack(&mapped, seat); } - pub fn toggle_sticky_current<'a>( - &mut self, - seats: impl Iterator>, - seat: &Seat, - ) { + pub fn toggle_sticky_current<'a>(&mut self, seat: &Seat) { let set = self.workspaces.sets.get_mut(&seat.active_output()).unwrap(); let workspace = &mut set.workspaces[set.active]; let maybe_window = workspace.focus_stack.get(seat).iter().next().cloned(); if let Some(mapped) = maybe_window { - self.toggle_sticky(seats, seat, &mapped); + self.toggle_sticky(seat, &mapped); } } @@ -3419,6 +3326,18 @@ impl Shell { let mut workspace_guard = self.workspace_state.update(); workspace_guard.add_workspace_state(workspace, WState::Urgent); } + + pub fn update_autotile(&mut self, autotile: bool) { + let mut guard = self.workspace_state.update(); + self.workspaces + .update_autotile(autotile, &mut guard, self.seats.iter()) + } + + pub fn update_autotile_behavior(&mut self, behavior: TileBehavior) { + let mut guard = self.workspace_state.update(); + self.workspaces + .update_autotile_behavior(behavior, &mut guard, self.seats.iter()) + } } fn workspace_set_idx<'a>( diff --git a/src/shell/seats.rs b/src/shell/seats.rs new file mode 100644 index 00000000..1c040959 --- /dev/null +++ b/src/shell/seats.rs @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use std::{any::Any, cell::RefCell, collections::HashMap, sync::Mutex, time::Duration}; + +use crate::{ + backend::render::cursor::{CursorShape, CursorState}, + config::{xkb_config_to_wl, Config}, + input::{ModifiersShortcutQueue, SupressedKeys}, + state::State, +}; +use smithay::{ + backend::input::{Device, DeviceCapability}, + desktop::utils::bbox_from_surface_tree, + input::{ + keyboard::{LedState, XkbConfig}, + pointer::{CursorIcon, CursorImageAttributes, CursorImageStatus}, + Seat, SeatState, + }, + output::Output, + reexports::{input::Device as InputDevice, wayland_server::DisplayHandle}, + utils::{Buffer, IsAlive, Monotonic, Point, Rectangle, Time, Transform}, + wayland::compositor::with_states, +}; +use tracing::warn; + +use super::grabs::{SeatMenuGrabState, SeatMoveGrabState}; + +crate::utils::id_gen!(next_seat_id, SEAT_ID, SEAT_IDS); + +#[derive(Debug)] +pub struct Seats { + seats: Vec>, + last_active: Option>, +} + +impl Seats { + pub fn new() -> Seats { + Seats { + seats: Vec::new(), + last_active: None, + } + } + + pub fn add_seat(&mut self, seat: Seat) { + if self.seats.is_empty() { + self.last_active = Some(seat.clone()); + } + self.seats.push(seat); + } + + pub fn remove_seat(&mut self, seat: &Seat) { + self.seats.retain(|s| s != seat); + if self.seats.is_empty() { + self.last_active = None; + } else if self.last_active.as_ref().is_some_and(|s| s == seat) { + self.last_active = Some(self.seats[0].clone()); + } + } + + pub fn iter(&self) -> impl Iterator> { + self.seats.iter() + } + + pub fn last_active(&self) -> &Seat { + self.last_active.as_ref().expect("No seat?") + } + + pub fn update_last_active(&mut self, seat: &Seat) { + self.last_active = Some(seat.clone()); + } + + pub fn for_device(&self, device: &D) -> Option<&Seat> { + self.iter().find(|seat| { + let userdata = seat.user_data(); + let devices = userdata.get::().unwrap(); + devices.has_device(device) + }) + } +} + +impl Devices { + pub fn add_device(&self, device: &D) -> Vec { + let id = device.id(); + let mut map = self.capabilities.borrow_mut(); + let caps = [ + DeviceCapability::Keyboard, + DeviceCapability::Pointer, + DeviceCapability::TabletTool, + ] + .iter() + .cloned() + .filter(|c| device.has_capability(*c)) + .collect::>(); + let new_caps = caps + .iter() + .cloned() + .filter(|c| map.values().flatten().all(|has| *c != *has)) + .collect::>(); + map.insert(id, caps); + + if device.has_capability(DeviceCapability::Keyboard) { + if let Some(device) = ::downcast_ref::(device) { + self.keyboards.borrow_mut().push(device.clone()); + } + } + + new_caps + } + + pub fn has_device(&self, device: &D) -> bool { + self.capabilities.borrow().contains_key(&device.id()) + } + + pub fn remove_device(&self, device: &D) -> Vec { + let id = device.id(); + + let mut keyboards = self.keyboards.borrow_mut(); + if let Some(idx) = keyboards.iter().position(|x| x.id() == id) { + keyboards.remove(idx); + } + + let mut map = self.capabilities.borrow_mut(); + map.remove(&id) + .unwrap_or(Vec::new()) + .into_iter() + .filter(|c| map.values().flatten().all(|has| *c != *has)) + .collect() + } + + pub fn update_led_state(&self, led_state: LedState) { + for keyboard in self.keyboards.borrow_mut().iter_mut() { + keyboard.led_update(led_state.into()); + } + } +} + +#[derive(Default)] +pub struct Devices { + capabilities: RefCell>>, + // Used for updating keyboard leds on kms backend + keyboards: RefCell>, +} + +impl Default for SeatId { + fn default() -> SeatId { + SeatId(next_seat_id()) + } +} + +impl Drop for SeatId { + fn drop(&mut self) { + SEAT_IDS.lock().unwrap().remove(&self.0); + } +} + +#[repr(transparent)] +struct SeatId(pub usize); +struct ActiveOutput(pub RefCell); + +pub fn create_seat( + dh: &DisplayHandle, + seat_state: &mut SeatState, + output: &Output, + config: &Config, + name: String, +) -> Seat { + let mut seat = seat_state.new_wl_seat(dh, name); + let userdata = seat.user_data(); + userdata.insert_if_missing(SeatId::default); + userdata.insert_if_missing(Devices::default); + userdata.insert_if_missing(SupressedKeys::default); + userdata.insert_if_missing(ModifiersShortcutQueue::default); + userdata.insert_if_missing(SeatMoveGrabState::default); + userdata.insert_if_missing(SeatMenuGrabState::default); + userdata.insert_if_missing(CursorState::default); + userdata.insert_if_missing(|| ActiveOutput(RefCell::new(output.clone()))); + userdata.insert_if_missing(|| RefCell::new(CursorImageStatus::default_named())); + + // A lot of clients bind keyboard and pointer unconditionally once on launch.. + // Initial clients might race the compositor on adding periheral and + // end up in a state, where they are not able to receive input. + // Additionally a lot of clients don't handle keyboards/pointer objects being + // removed very well either and we don't want to crash applications, because the + // user is replugging their keyboard or mouse. + // + // So instead of doing the right thing (and initialize these capabilities as matching + // devices appear), we have to surrender to reality and just always expose a keyboard and pointer. + let conf = config.xkb_config(); + if let Err(err) = seat.add_keyboard(xkb_config_to_wl(&conf), 600, 25) { + warn!( + ?err, + "Failed to load provided xkb config. Trying default...", + ); + seat.add_keyboard(XkbConfig::default(), 600, 25) + .expect("Failed to load xkb configuration files"); + } + seat.add_pointer(); + seat.add_touch(); + + seat +} + +pub trait SeatExt { + fn id(&self) -> usize; + + fn active_output(&self) -> Output; + fn set_active_output(&self, output: &Output); + fn devices(&self) -> &Devices; + + fn cursor_geometry( + &self, + loc: impl Into>, + time: Time, + ) -> Option<(Rectangle, Point)>; +} + +impl SeatExt for Seat { + fn id(&self) -> usize { + self.user_data().get::().unwrap().0 + } + + fn active_output(&self) -> Output { + self.user_data() + .get::() + .map(|x| x.0.borrow().clone()) + .unwrap() + } + + fn set_active_output(&self, output: &Output) { + *self + .user_data() + .get::() + .unwrap() + .0 + .borrow_mut() = output.clone(); + } + + fn devices(&self) -> &Devices { + self.user_data().get::().unwrap() + } + + fn cursor_geometry( + &self, + loc: impl Into>, + time: Time, + ) -> Option<(Rectangle, Point)> { + let location = loc.into().to_i32_round(); + + let cursor_status = self + .user_data() + .get::>() + .map(|cell| { + let mut cursor_status = cell.borrow_mut(); + if let CursorImageStatus::Surface(ref surface) = *cursor_status { + if !surface.alive() { + *cursor_status = CursorImageStatus::default_named(); + } + } + cursor_status.clone() + }) + .unwrap_or(CursorImageStatus::default_named()); + + match cursor_status { + CursorImageStatus::Surface(surface) => { + let hotspot = with_states(&surface, |states| { + states + .data_map + .get::>() + .unwrap() + .lock() + .unwrap() + .hotspot + }); + let geo = bbox_from_surface_tree(&surface, (location.x, location.y)); + let buffer_geo = Rectangle::from_loc_and_size( + (geo.loc.x, geo.loc.y), + geo.size.to_buffer(1, Transform::Normal), + ); + Some((buffer_geo, (hotspot.x, hotspot.y).into())) + } + CursorImageStatus::Named(CursorIcon::Default) => { + let seat_userdata = self.user_data(); + seat_userdata.insert_if_missing(CursorState::default); + let state = seat_userdata.get::().unwrap(); + let frame = state + .cursors + .get(&CursorShape::Default) + .unwrap() + .get_image(1, Into::::into(time).as_millis() as u32); + + Some(( + Rectangle::from_loc_and_size( + location, + (frame.width as i32, frame.height as i32), + ), + (frame.xhot as i32, frame.yhot as i32).into(), + )) + } + CursorImageStatus::Named(_) => { + // TODO: Handle for `cursor_shape_v1` protocol + None + } + CursorImageStatus::Hidden => None, + } + } +} diff --git a/src/state.rs b/src/state.rs index 02c2cd5e..0df36ae0 100644 --- a/src/state.rs +++ b/src/state.rs @@ -3,9 +3,8 @@ use crate::{ backend::{kms::KmsState, winit::WinitState, x11::X11State}, config::{Config, OutputConfig}, - input::{gestures::GestureState, Devices}, - shell::{grabs::SeatMoveGrabState, Shell}, - utils::prelude::*, + input::gestures::GestureState, + shell::{grabs::SeatMoveGrabState, SeatExt, Shell}, wayland::protocols::{ drm::WlDrmState, image_source::ImageSourceState, output_configuration::OutputConfigurationState, screencopy::ScreencopyState, @@ -25,7 +24,6 @@ use smithay::{ backend::{ allocator::dmabuf::Dmabuf, drm::DrmNode, - input::Device, renderer::{ element::{ default_primary_scanout_output_compare, utils::select_dmabuf_feedback, @@ -44,7 +42,7 @@ use smithay::{ with_surfaces_surface_tree, OutputPresentationFeedback, }, }, - input::{pointer::CursorImageStatus, Seat, SeatState}, + input::{pointer::CursorImageStatus, SeatState}, output::{Mode as OutputMode, Output, Scale}, reexports::{ calloop::{LoopHandle, LoopSignal}, @@ -149,10 +147,7 @@ pub struct Common { pub event_loop_handle: LoopHandle<'static, State>, pub event_loop_signal: LoopSignal, - //pub output_conf: ConfigurationManager, pub shell: Shell, - seats: Vec>, - last_active_seat: Option>, pub clock: Clock, pub should_stop: bool, @@ -229,12 +224,11 @@ impl BackendData { output: &Output, test_only: bool, shell: &mut Shell, - seats: impl Iterator>, loop_handle: &LoopHandle<'_, State>, ) -> Result<(), anyhow::Error> { let result = match self { BackendData::Kms(ref mut state) => { - state.apply_config_for_output(output, seats, shell, test_only, loop_handle) + state.apply_config_for_output(output, shell, test_only, loop_handle) } BackendData::Winit(ref mut state) => state.apply_config_for_output(output, test_only), BackendData::X11(ref mut state) => state.apply_config_for_output(output, test_only), @@ -411,8 +405,6 @@ impl State { shell, - seats: Vec::new(), - last_active_seat: None, local_offset, clock, @@ -495,38 +487,6 @@ impl State { } impl Common { - pub fn add_seat(&mut self, seat: Seat) { - if self.seats.is_empty() { - self.last_active_seat = Some(seat.clone()); - } - self.seats.push(seat); - } - - pub fn remove_seat(&mut self, seat: &Seat) { - self.seats.retain(|s| s != seat); - if self.seats.is_empty() { - self.last_active_seat = None; - } else if self.last_active_seat() == seat { - self.last_active_seat = Some(self.seats[0].clone()); - } - } - - pub fn seats(&self) -> impl Iterator> { - self.seats.iter() - } - - pub fn seat_with_device(&self, device: &D) -> Option<&Seat> { - self.seats().find(|seat| { - let userdata = seat.user_data(); - let devices = userdata.get::().unwrap(); - devices.has_device(device) - }) - } - - pub fn last_active_seat(&self) -> &Seat { - self.last_active_seat.as_ref().expect("No seat?") - } - pub fn send_frames( &self, output: &Output, @@ -573,6 +533,7 @@ impl Common { } for seat in self + .shell .seats .iter() .filter(|seat| &seat.active_output() == output) diff --git a/src/utils/prelude.rs b/src/utils/prelude.rs index bbedcba4..2503fee1 100644 --- a/src/utils/prelude.rs +++ b/src/utils/prelude.rs @@ -1,22 +1,10 @@ -use std::{cell::RefCell, sync::Mutex, time::Duration}; - -use crate::{ - backend::render::cursor::{CursorShape, CursorState}, - input::{ActiveOutput, SeatId}, -}; use smithay::{ - desktop::utils::bbox_from_surface_tree, - input::{ - pointer::{CursorIcon, CursorImageAttributes, CursorImageStatus}, - Seat, - }, output::Output, - utils::{Buffer, IsAlive, Monotonic, Point, Rectangle, Time, Transform}, - wayland::compositor::with_states, + utils::{Rectangle, Transform}, }; pub use super::geometry::*; -pub use crate::shell::{Shell, Workspace}; +pub use crate::shell::{SeatExt, Shell, Workspace}; pub use crate::state::{Common, State}; pub use crate::wayland::handlers::xdg_shell::popup::update_reactive_popups; @@ -40,102 +28,3 @@ impl OutputExt for Output { .as_global() } } - -pub trait SeatExt { - fn id(&self) -> usize; - - fn active_output(&self) -> Output; - fn set_active_output(&self, output: &Output); - fn cursor_geometry( - &self, - loc: impl Into>, - time: Time, - ) -> Option<(Rectangle, Point)>; -} - -impl SeatExt for Seat { - fn id(&self) -> usize { - self.user_data().get::().unwrap().0 - } - - fn active_output(&self) -> Output { - self.user_data() - .get::() - .map(|x| x.0.borrow().clone()) - .unwrap() - } - - fn set_active_output(&self, output: &Output) { - *self - .user_data() - .get::() - .unwrap() - .0 - .borrow_mut() = output.clone(); - } - - fn cursor_geometry( - &self, - loc: impl Into>, - time: Time, - ) -> Option<(Rectangle, Point)> { - let location = loc.into().to_i32_round(); - - let cursor_status = self - .user_data() - .get::>() - .map(|cell| { - let mut cursor_status = cell.borrow_mut(); - if let CursorImageStatus::Surface(ref surface) = *cursor_status { - if !surface.alive() { - *cursor_status = CursorImageStatus::default_named(); - } - } - cursor_status.clone() - }) - .unwrap_or(CursorImageStatus::default_named()); - - match cursor_status { - CursorImageStatus::Surface(surface) => { - let hotspot = with_states(&surface, |states| { - states - .data_map - .get::>() - .unwrap() - .lock() - .unwrap() - .hotspot - }); - let geo = bbox_from_surface_tree(&surface, (location.x, location.y)); - let buffer_geo = Rectangle::from_loc_and_size( - (geo.loc.x, geo.loc.y), - geo.size.to_buffer(1, Transform::Normal), - ); - Some((buffer_geo, (hotspot.x, hotspot.y).into())) - } - CursorImageStatus::Named(CursorIcon::Default) => { - let seat_userdata = self.user_data(); - seat_userdata.insert_if_missing(CursorState::default); - let state = seat_userdata.get::().unwrap(); - let frame = state - .cursors - .get(&CursorShape::Default) - .unwrap() - .get_image(1, Into::::into(time).as_millis() as u32); - - Some(( - Rectangle::from_loc_and_size( - location, - (frame.width as i32, frame.height as i32), - ), - (frame.xhot as i32, frame.yhot as i32).into(), - )) - } - CursorImageStatus::Named(_) => { - // TODO: Handle for `cursor_shape_v1` protocol - None - } - CursorImageStatus::Hidden => None, - } - } -} diff --git a/src/wayland/handlers/compositor.rs b/src/wayland/handlers/compositor.rs index 3465ca86..cb36a002 100644 --- a/src/wayland/handlers/compositor.rs +++ b/src/wayland/handlers/compositor.rs @@ -78,7 +78,10 @@ impl State { }); if !initial_configure_sent { // compute initial dimensions by mapping - Shell::map_layer(self, &surface); + if let Some(target) = self.common.shell.map_layer(&surface) { + let seat = self.common.shell.seats.last_active().clone(); + Shell::set_focus(self, Some(&target), &seat, None); + } surface.layer_surface().send_configure(); } initial_configure_sent @@ -159,7 +162,14 @@ impl CompositorHandler for State { .unwrap_or(false) { window.on_commit(); - Shell::map_window(self, &window); + if let Some(target) = self.common.shell.map_window( + &window, + &self.common.event_loop_handle, + &self.common.theme, + ) { + let seat = self.common.shell.seats.last_active().clone(); + Shell::set_focus(self, Some(&target), &seat, None); + } } } } @@ -187,7 +197,7 @@ impl CompositorHandler for State { // session-lock disallows null commits // if it was a move-grab? - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); let moved_window = seat .user_data() .get::() diff --git a/src/wayland/handlers/fractional_scale.rs b/src/wayland/handlers/fractional_scale.rs index c58e6ce1..e79e5b27 100644 --- a/src/wayland/handlers/fractional_scale.rs +++ b/src/wayland/handlers/fractional_scale.rs @@ -45,10 +45,7 @@ impl FractionalScaleHandler for State { .cloned() }) }) - .unwrap_or_else(|| { - let seat = self.common.last_active_seat(); - seat.active_output() - }); + .unwrap_or_else(|| self.common.shell.seats.last_active().active_output()); with_states(&surface, |states| { with_fractional_scale(states, |fractional_scale| { diff --git a/src/wayland/handlers/layer_shell.rs b/src/wayland/handlers/layer_shell.rs index 22808e58..49fd2f04 100644 --- a/src/wayland/handlers/layer_shell.rs +++ b/src/wayland/handlers/layer_shell.rs @@ -26,7 +26,7 @@ impl WlrLayerShellHandler for State { _layer: Layer, namespace: String, ) { - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); let output = wl_output .as_ref() .and_then(Output::from_resource) diff --git a/src/wayland/handlers/output_configuration.rs b/src/wayland/handlers/output_configuration.rs index ab32c5af..2880be8b 100644 --- a/src/wayland/handlers/output_configuration.rs +++ b/src/wayland/handlers/output_configuration.rs @@ -82,12 +82,10 @@ impl State { } } - let seats = self.common.seats().cloned().collect::>(); if let Err(err) = self.backend.apply_config_for_output( output, test_only, &mut self.common.shell, - seats.iter().cloned(), &self.common.event_loop_handle, ) { warn!( @@ -109,7 +107,6 @@ impl State { output, false, &mut self.common.shell, - seats.iter().cloned(), &self.common.event_loop_handle, ) { error!(?err, "Failed to reset output config for {}.", output.name(),); diff --git a/src/wayland/handlers/screencopy/mod.rs b/src/wayland/handlers/screencopy/mod.rs index 539a885a..3e3f0cff 100644 --- a/src/wayland/handlers/screencopy/mod.rs +++ b/src/wayland/handlers/screencopy/mod.rs @@ -64,7 +64,9 @@ impl ScreencopyHandler for State { fn capture_cursor_source(&mut self, _source: &ImageSourceData) -> Option { let size = if let Some((geometry, _)) = self .common - .last_active_seat() + .shell + .seats + .last_active() .cursor_geometry((0.0, 0.0), self.common.clock.now()) { geometry.size @@ -125,7 +127,7 @@ impl ScreencopyHandler for State { } fn new_cursor_session(&mut self, session: CursorSession) { let (pointer_loc, pointer_size, hotspot) = { - let seat = self.common.last_active_seat(); + let seat = self.common.shell.seats.last_active(); let pointer = seat.get_pointer().unwrap(); let pointer_loc = pointer.current_location().to_i32_round().as_global(); @@ -266,7 +268,7 @@ impl ScreencopyHandler for State { return; } - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); render_cursor_to_buffer(self, &session, frame, &seat); } diff --git a/src/wayland/handlers/screencopy/render.rs b/src/wayland/handlers/screencopy/render.rs index 43affc0c..cb7ee76e 100644 --- a/src/wayland/handlers/screencopy/render.rs +++ b/src/wayland/handlers/screencopy/render.rs @@ -286,7 +286,10 @@ pub fn render_workspace_to_buffer( dt, age, additional_damage, - common, + &common.shell, + &common.config, + &common.theme, + common.clock.now(), &output, None, handle, @@ -311,7 +314,10 @@ pub fn render_workspace_to_buffer( dt, age, additional_damage, - common, + &common.shell, + &common.config, + &common.theme, + common.clock.now(), &output, None, handle, @@ -529,7 +535,7 @@ pub fn render_window_to_buffer( .map(Into::>::into), ); - let seat = common.last_active_seat().clone(); + let seat = common.shell.seats.last_active().clone(); if let Some(location) = { if let Some(mapped) = common.shell.element_for_surface(window) { mapped.cursor_position(&seat).and_then(|mut p| { diff --git a/src/wayland/handlers/seat.rs b/src/wayland/handlers/seat.rs index f959e40e..a3b1aeb7 100644 --- a/src/wayland/handlers/seat.rs +++ b/src/wayland/handlers/seat.rs @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - input::Devices, shell::focus::target::{KeyboardFocusTarget, PointerFocusTarget}, + shell::Devices, state::State, }; use smithay::{ diff --git a/src/wayland/handlers/toplevel_management.rs b/src/wayland/handlers/toplevel_management.rs index 33a6d1eb..b61bbf8a 100644 --- a/src/wayland/handlers/toplevel_management.rs +++ b/src/wayland/handlers/toplevel_management.rs @@ -53,7 +53,7 @@ impl ToplevelManagementHandler for State { .any(|w| &w == window) }); if let Some((idx, workspace)) = maybe { - let seat = seat.unwrap_or(self.common.last_active_seat().clone()); + let seat = seat.unwrap_or(self.common.shell.seats.last_active().clone()); let mapped = workspace .mapped() .find(|m| m.windows().any(|(w, _)| &w == window)) @@ -66,7 +66,7 @@ impl ToplevelManagementHandler for State { WorkspaceDelta::new_shortcut(), ); // TODO: Move pointer? mapped.focus_window(window); - Common::set_focus(self, Some(&mapped.clone().into()), &seat, None); + Shell::set_focus(self, Some(&mapped.clone().into()), &seat, None); return; } } @@ -104,7 +104,17 @@ impl ToplevelManagementHandler for State { .unwrap() .clone(); let from_handle = from_workspace.handle; - let _ = Shell::move_window(self, None, &mapped, &from_handle, &to_handle, false, None); + let seat = self.common.shell.seats.last_active().clone(); + if let Some((target, _)) = self.common.shell.move_window( + Some(&seat), + &mapped, + &from_handle, + &to_handle, + false, + None, + ) { + Shell::set_focus(self, Some(&target), &seat, None); + } return; } } @@ -115,7 +125,7 @@ impl ToplevelManagementHandler for State { window: &::Window, output: Option, ) { - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); if let Some(mapped) = self.common.shell.element_for_surface(window).cloned() { if let Some(output) = output { let from = self @@ -177,7 +187,7 @@ impl ToplevelManagementHandler for State { fn maximize(&mut self, _dh: &DisplayHandle, window: &::Window) { if let Some(mapped) = self.common.shell.element_for_surface(window).cloned() { - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); self.common.shell.maximize_request(&mapped, &seat); } } @@ -198,7 +208,7 @@ impl ToplevelManagementHandler for State { fn unminimize(&mut self, _dh: &DisplayHandle, window: &::Window) { if let Some(mut mapped) = self.common.shell.element_for_surface(window).cloned() { - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); self.common.shell.unminimize_request(&mapped, &seat); if mapped.is_stack() { mapped.stack_ref_mut().unwrap().set_active(window); diff --git a/src/wayland/handlers/workspace.rs b/src/wayland/handlers/workspace.rs index 6897f8b4..eafbef0e 100644 --- a/src/wayland/handlers/workspace.rs +++ b/src/wayland/handlers/workspace.rs @@ -47,7 +47,7 @@ impl WorkspaceHandler for State { } } Request::SetTilingState { workspace, state } => { - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); if let Some(workspace) = self .common .shell diff --git a/src/wayland/handlers/xdg_activation.rs b/src/wayland/handlers/xdg_activation.rs index c3a94e60..7b7147c9 100644 --- a/src/wayland/handlers/xdg_activation.rs +++ b/src/wayland/handlers/xdg_activation.rs @@ -115,7 +115,7 @@ impl XdgActivationHandler for State { } } ActivationContext::Workspace(workspace) => { - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); let current_output = seat.active_output(); if element.is_minimized() { @@ -159,7 +159,7 @@ impl XdgActivationHandler for State { .space_for(&element) .map(|w| w.handle.clone()) { - Shell::append_focus_stack(self, &element, &seat); + self.common.shell.append_focus_stack(&element, &seat); self.common.shell.set_urgent(&w); } } diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index fb28cbab..fa6de433 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -41,7 +41,7 @@ impl XdgShellHandler for State { } fn new_toplevel(&mut self, surface: ToplevelSurface) { - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); let window = CosmicSurface::from(surface); self.common.shell.pending_windows.push((window, seat, None)); // We will position the window after the first commit, when we know its size hints @@ -90,7 +90,7 @@ impl XdgShellHandler for State { grab.ungrab(PopupUngrabStrategy::All); return; } - Common::set_focus(self, grab.current_grab().as_ref(), &seat, Some(serial)); + Shell::set_focus(self, grab.current_grab().as_ref(), &seat, Some(serial)); keyboard.set_grab(PopupKeyboardGrab::new(&grab), serial); } @@ -183,7 +183,7 @@ impl XdgShellHandler for State { .element_for_surface(surface.wl_surface()) .cloned() { - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); self.common.shell.maximize_request(&mapped, &seat) } } @@ -200,7 +200,7 @@ impl XdgShellHandler for State { } fn fullscreen_request(&mut self, surface: ToplevelSurface, output: Option) { - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); let active_output = seat.active_output(); let output = output .as_ref() @@ -378,7 +378,7 @@ impl XdgShellHandler for State { } fn toplevel_destroyed(&mut self, surface: ToplevelSurface) { - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); self.common.shell.unmap_surface(surface.wl_surface(), &seat); let output = self diff --git a/src/xwayland.rs b/src/xwayland.rs index 64c9e146..1b6c9a1e 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -134,7 +134,7 @@ impl State { impl Common { fn is_x_focused(&self, xwm: XwmId) -> bool { - if let Some(keyboard) = self.last_active_seat().get_keyboard() { + if let Some(keyboard) = self.shell.seats.last_active().get_keyboard() { if let Some(KeyboardFocusTarget::Element(mapped)) = keyboard.current_focus() { if let Some(surface) = mapped.active_window().x11_surface() { return surface.xwm_id().unwrap() == xwm; @@ -146,7 +146,7 @@ impl Common { } pub fn update_x11_stacking_order(&mut self) { - let active_output = self.last_active_seat().active_output(); + let active_output = self.shell.seats.last_active().active_output(); if let Some(xwm) = self .shell .xwayland_state @@ -254,7 +254,7 @@ impl XwmHandler for State { return; } - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); if let Some(context) = startup_id .map(XdgActivationToken::from) .and_then(|token| { @@ -308,7 +308,14 @@ impl XwmHandler for State { } } } - Shell::map_window(self, &window); + if let Some(target) = self.common.shell.map_window( + &window, + &self.common.event_loop_handle, + &self.common.theme, + ) { + let seat = self.common.shell.seats.last_active().clone(); + Shell::set_focus(self, Some(&target), &seat, None); + } } } @@ -322,7 +329,7 @@ impl XwmHandler for State { { return; } - Shell::map_override_redirect(self, window) + self.common.shell.map_override_redirect(window) } fn unmapped_window(&mut self, _xwm: XwmId, window: X11Surface) { @@ -332,7 +339,7 @@ impl XwmHandler for State { .override_redirect_windows .retain(|or| or != &window); } else { - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); self.common.shell.unmap_surface(&window, &seat); } @@ -459,14 +466,14 @@ impl XwmHandler for State { resize_edge: smithay::xwayland::xwm::ResizeEdge, ) { if let Some(wl_surface) = window.wl_surface() { - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); Shell::resize_request(self, &wl_surface, &seat, None, resize_edge.into()) } } fn move_request(&mut self, _xwm: XwmId, window: X11Surface, _button: u32) { if let Some(wl_surface) = window.wl_surface() { - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); Shell::move_request( self, &wl_surface, @@ -480,7 +487,7 @@ impl XwmHandler for State { fn maximize_request(&mut self, _xwm: XwmId, window: X11Surface) { if let Some(mapped) = self.common.shell.element_for_surface(&window).cloned() { - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); self.common.shell.maximize_request(&mapped, &seat); } } @@ -501,7 +508,7 @@ impl XwmHandler for State { fn unminimize_request(&mut self, _xwm: XwmId, window: X11Surface) { if let Some(mut mapped) = self.common.shell.element_for_surface(&window).cloned() { - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); self.common.shell.unminimize_request(&mapped, &seat); if mapped.is_stack() { let maybe_surface = mapped.windows().find(|(w, _)| w.is_window(&window)); @@ -513,7 +520,7 @@ impl XwmHandler for State { } fn fullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) { - let seat = self.common.last_active_seat().clone(); + let seat = self.common.shell.seats.last_active().clone(); if let Some(mapped) = self.common.shell.element_for_surface(&window).cloned() { if let Some((output, handle)) = self .common @@ -539,7 +546,7 @@ impl XwmHandler for State { } } } else { - let output = self.common.last_active_seat().active_output(); + let output = seat.active_output(); if let Some(o) = self .common .shell @@ -573,7 +580,7 @@ impl XwmHandler for State { mime_type: String, fd: OwnedFd, ) { - let seat = self.common.last_active_seat(); + let seat = self.common.shell.seats.last_active(); match selection { SelectionTarget::Clipboard => { if let Err(err) = request_data_device_client_selection(seat, mime_type, fd) { @@ -602,7 +609,7 @@ impl XwmHandler for State { trace!(?selection, ?mime_types, "Got Selection from Xwayland",); if self.common.is_x_focused(xwm) { - let seat = self.common.last_active_seat(); + let seat = self.common.shell.seats.last_active(); match selection { SelectionTarget::Clipboard => { set_data_device_selection(&self.common.display_handle, &seat, mime_types, xwm) @@ -615,7 +622,7 @@ impl XwmHandler for State { } fn cleared_selection(&mut self, xwm: XwmId, selection: SelectionTarget) { - for seat in self.common.seats() { + for seat in self.common.shell.seats.iter() { match selection { SelectionTarget::Clipboard => { if current_data_device_selection_userdata(seat).as_deref() == Some(&xwm) {