From 5d5a51069197e5f044ab1ce116b342a679b3aadd Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 10 Apr 2024 15:49:08 +0200 Subject: [PATCH] state: Move shell behind RwLock --- src/backend/kms/mod.rs | 127 +- src/backend/mod.rs | 15 +- src/backend/winit.rs | 28 +- src/backend/x11.rs | 30 +- src/config/mod.rs | 72 +- src/input/mod.rs | 958 +++++++----- src/main.rs | 44 +- src/session.rs | 8 +- src/shell/element/stack.rs | 121 +- src/shell/element/window.rs | 76 +- src/shell/focus/mod.rs | 45 +- src/shell/focus/target.rs | 18 +- src/shell/grabs/menu/default.rs | 218 ++- src/shell/grabs/menu/mod.rs | 18 +- src/shell/grabs/mod.rs | 9 + src/shell/grabs/moving.rs | 57 +- src/shell/layout/floating/grabs/resize.rs | 7 + src/shell/layout/tiling/grabs/resize.rs | 10 +- src/shell/layout/tiling/grabs/swap.rs | 2 +- src/shell/layout/tiling/mod.rs | 128 +- src/shell/mod.rs | 1315 +++++++---------- src/shell/seats.rs | 10 + src/shell/workspace.rs | 16 +- src/state.rs | 107 +- src/systemd.rs | 1 - src/theme.rs | 13 +- src/wayland/handlers/compositor.rs | 158 +- src/wayland/handlers/decoration.rs | 112 +- src/wayland/handlers/fractional_scale.rs | 12 +- src/wayland/handlers/input_method.rs | 9 +- src/wayland/handlers/layer_shell.rs | 23 +- src/wayland/handlers/output_configuration.rs | 17 +- src/wayland/handlers/screencopy/mod.rs | 57 +- src/wayland/handlers/screencopy/render.rs | 48 +- src/wayland/handlers/selection.rs | 2 - src/wayland/handlers/session_lock.rs | 16 +- src/wayland/handlers/toplevel_info.rs | 4 +- src/wayland/handlers/toplevel_management.rs | 162 +- src/wayland/handlers/workspace.rs | 25 +- src/wayland/handlers/xdg_activation.rs | 46 +- src/wayland/handlers/xdg_shell/mod.rs | 225 +-- .../handlers/xwayland_keyboard_grab.rs | 6 +- src/wayland/protocols/toplevel_info.rs | 48 +- src/wayland/protocols/toplevel_management.rs | 47 +- src/xwayland.rs | 236 +-- 45 files changed, 2633 insertions(+), 2073 deletions(-) diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index e512da1b..7d259c74 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -11,8 +11,9 @@ use crate::{ utils::prelude::*, wayland::{ handlers::screencopy::{submit_buffer, FrameHolder, SessionData}, - protocols::screencopy::{ - FailureReason, Frame as ScreencopyFrame, Session as ScreencopySession, + protocols::{ + screencopy::{FailureReason, Frame as ScreencopyFrame, Session as ScreencopySession}, + workspace::WorkspaceUpdateGuard, }, }, }; @@ -76,6 +77,7 @@ use smithay::{ relative_pointer::RelativePointerManagerState, seat::WaylandFocus, shm::{shm_format_to_fourcc, with_buffer_contents}, + xdg_activation::XdgActivationState, }, }; use tracing::{error, info, trace, warn}; @@ -202,7 +204,7 @@ pub fn init_backend( state.backend.kms().input_devices.remove(device.name()); } state.process_input_event(event, true); - for output in state.common.shell.outputs() { + for output in state.common.shell.read().unwrap().outputs() { if let Err(err) = state.backend.kms().schedule_render( &state.common.event_loop_handle, output, @@ -326,9 +328,13 @@ pub fn init_backend( state.common.config.read_outputs( &mut state.common.output_configuration_state, &mut state.backend, - &mut state.common.shell, + &mut state.common.shell.write().unwrap(), &state.common.event_loop_handle, + &mut state.common.workspace_state.update(), + &state.common.xdg_activation_state, ); + state.common.refresh(); + for surface in state .backend .kms() @@ -339,7 +345,7 @@ pub fn init_backend( surface.scheduled = false; surface.pending = false; } - for output in state.common.shell.outputs() { + for output in state.common.shell.read().unwrap().outputs() { if let Err(err) = state.backend.kms().schedule_render( &state.common.event_loop_handle, output, @@ -601,7 +607,7 @@ impl State { let outputs = device.enumerate_surfaces()?.added; // There are no removed outputs on newly added devices let mut wl_outputs = Vec::new(); - let mut w = self.common.shell.global_space().size.w; + let mut w = self.common.shell.read().unwrap().global_space().size.w; { let backend = self.backend.kms(); backend @@ -701,9 +707,12 @@ impl State { self.common.config.read_outputs( &mut self.common.output_configuration_state, &mut self.backend, - &mut self.common.shell, + &mut *self.common.shell.write().unwrap(), &self.common.event_loop_handle, + &mut self.common.workspace_state.update(), + &self.common.xdg_activation_state, ); + self.common.refresh(); Ok(()) } @@ -720,7 +729,7 @@ impl State { let backend = self.backend.kms(); if let Some(device) = backend.devices.get_mut(&drm_node) { let changes = device.enumerate_surfaces()?; - let mut w = self.common.shell.global_space().size.w; + let mut w = self.common.shell.read().unwrap().global_space().size.w; for crtc in changes.removed { if let Some(pos) = device .non_desktop_connectors @@ -831,18 +840,23 @@ impl State { self.common .output_configuration_state .add_heads(outputs_added.iter()); - self.common.config.read_outputs( - &mut self.common.output_configuration_state, - &mut self.backend, - &mut self.common.shell, - &self.common.event_loop_handle, - ); - // Don't remove the outputs, before potentially new ones have been initialized. - // reading a new config means outputs might become enabled, that were previously disabled. - // 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); + { + self.common.config.read_outputs( + &mut self.common.output_configuration_state, + &mut self.backend, + &mut *self.common.shell.write().unwrap(), + &self.common.event_loop_handle, + &mut self.common.workspace_state.update(), + &self.common.xdg_activation_state, + ); + self.common.refresh(); + // Don't remove the outputs, before potentially new ones have been initialized. + // reading a new config means outputs might become enabled, that were previously disabled. + // 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.remove_output(&output); + } } { @@ -889,14 +903,17 @@ impl State { if self.backend.kms().session.is_active() { for output in outputs_removed { - self.common.shell.remove_output(&output); + self.common.remove_output(&output); } self.common.config.read_outputs( &mut self.common.output_configuration_state, &mut self.backend, - &mut self.common.shell, + &mut *self.common.shell.write().unwrap(), &self.common.event_loop_handle, + &mut self.common.workspace_state.update(), + &self.common.xdg_activation_state, ); + self.common.refresh(); } else { self.common.output_configuration_state.update(); } @@ -1178,30 +1195,33 @@ impl Surface { ); } - let (previous_workspace, workspace) = state.shell.workspaces.active(&self.output); - let (previous_idx, idx) = state.shell.workspaces.active_num(&self.output); - let previous_workspace = previous_workspace - .zip(previous_idx) - .map(|((w, start), idx)| (w.handle, idx, start)); - let workspace = (workspace.handle, idx); - - let mut elements = workspace_elements( - Some(&render_node), - &mut renderer, - &state.shell, - &state.config, - &state.theme, - state.clock.now(), - &self.output, - previous_workspace, - workspace, - CursorMode::All, - &mut Some(&mut self.fps), - false, - ) - .map_err(|err| { - anyhow::format_err!("Failed to accumulate elements for rendering: {:?}", err) - })?; + let mut elements = { + let shell = state.shell.read().unwrap(); + let (previous_workspace, workspace) = shell.workspaces.active(&self.output); + let (previous_idx, idx) = shell.workspaces.active_num(&self.output); + let previous_workspace = previous_workspace + .zip(previous_idx) + .map(|((w, start), idx)| (w.handle, idx, start)); + let workspace = (workspace.handle, idx); + + workspace_elements( + Some(&render_node), + &mut renderer, + &*shell, + &state.config, + &state.theme, + state.clock.now(), + &self.output, + previous_workspace, + workspace, + CursorMode::All, + &mut Some(&mut self.fps), + false, + ) + .map_err(|err| { + anyhow::format_err!("Failed to accumulate elements for rendering: {:?}", err) + })? + }; self.fps.elements(); let frames: Vec<( @@ -1490,6 +1510,8 @@ impl KmsState { shell: &mut Shell, test_only: bool, loop_handle: &LoopHandle<'_, State>, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, + xdg_activation_state: &XdgActivationState, ) -> Result<(), anyhow::Error> { let recreated = if let Some(device) = self .devices @@ -1509,7 +1531,12 @@ impl KmsState { if !output_config.enabled { if !test_only { - shell.remove_output(output); + shell.workspaces.remove_output( + output, + shell.seats.iter(), + workspace_state, + xdg_activation_state, + ); if surface.surface.take().is_some() { // just drop it surface.pending = false; @@ -1608,7 +1635,9 @@ impl KmsState { surface.surface = Some(target); true }; - shell.add_output(output); + shell + .workspaces + .add_output(output, workspace_state, xdg_activation_state); res } else { false @@ -1748,7 +1777,7 @@ impl KmsState { &surface.output, backend.primary_node, target_node, - &common.shell, + &*common.shell.read().unwrap(), ); let result = if render_node != target_node { diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 688bb8ce..710b91ba 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -45,17 +45,26 @@ pub fn init_backend_auto( let output = state .common .shell + .read() + .unwrap() .outputs() .next() - .with_context(|| "Backend initialized without output")?; + .with_context(|| "Backend initialized without output") + .cloned()?; let initial_seat = crate::shell::create_seat( dh, &mut state.common.seat_state, - output, + &output, &state.common.config, "seat-0".into(), ); - state.common.shell.seats.add_seat(initial_seat); + state + .common + .shell + .write() + .unwrap() + .seats + .add_seat(initial_seat); } res } diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 2746d4f8..fe41f480 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -63,7 +63,7 @@ impl WinitState { surface.clone(), &mut self.damage_tracker, age, - &state.shell, + &*state.shell.read().unwrap(), &state.config, &state.theme, state.clock.now(), @@ -201,7 +201,7 @@ pub fn init_backend( } PumpStatus::Exit(_) => { let output = state.backend.winit().output.clone(); - state.common.shell.remove_output(&output); + state.common.remove_output(&output); if let Some(token) = token.take() { event_loop_handle.remove(token); } @@ -226,13 +226,21 @@ pub fn init_backend( .common .output_configuration_state .add_heads(std::iter::once(&output)); - state.common.shell.add_output(&output); - state.common.config.read_outputs( - &mut state.common.output_configuration_state, - &mut state.backend, - &mut state.common.shell, - &state.common.event_loop_handle, - ); + { + state.common.add_output(&output); + let mut shell = state.common.shell.write().unwrap(); + state.common.config.read_outputs( + &mut state.common.output_configuration_state, + &mut state.backend, + &mut *shell, + &state.common.event_loop_handle, + &mut state.common.workspace_state.update(), + &state.common.xdg_activation_state, + ); + + std::mem::drop(shell); + state.common.refresh(); + } state.launch_xwayland(None); Ok(()) @@ -299,7 +307,7 @@ impl State { // here we can handle special cases for winit inputs match event { WinitEvent::Focus(true) => { - for seat in self.common.shell.seats.iter() { + for seat in self.common.shell.read().unwrap().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 8c7eff3d..d3e90b0c 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -222,7 +222,7 @@ impl Surface { buffer.clone(), &mut self.damage_tracker, age as usize, - &state.shell, + &*state.shell.read().unwrap(), &state.config, &state.theme, state.clock.now(), @@ -364,13 +364,21 @@ pub fn init_backend( .common .output_configuration_state .add_heads(std::iter::once(&output)); - state.common.shell.add_output(&output); - state.common.config.read_outputs( - &mut state.common.output_configuration_state, - &mut state.backend, - &mut state.common.shell, - &state.common.event_loop_handle, - ); + { + state.common.add_output(&output); + let mut shell = state.common.shell.write().unwrap(); + state.common.config.read_outputs( + &mut state.common.output_configuration_state, + &mut state.backend, + &mut *shell, + &state.common.event_loop_handle, + &mut state.common.workspace_state.update(), + &state.common.xdg_activation_state, + ); + + std::mem::drop(shell); + state.common.refresh(); + } state.launch_xwayland(None); event_loop @@ -395,7 +403,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); + state.common.remove_output(&output); } } X11Event::Resized { @@ -504,7 +512,7 @@ impl State { .unwrap(); let device = event.device(); - for seat in self.common.shell.seats.iter() { + for seat in self.common.shell.read().unwrap().seats.iter() { let devices = seat.user_data().get::().unwrap(); if devices.has_device(&device) { seat.set_active_output(&output); @@ -518,7 +526,7 @@ impl State { self.process_input_event(event, false); // TODO actually figure out the output - for output in self.common.shell.outputs() { + for output in self.common.shell.read().unwrap().outputs() { self.backend.x11().schedule_render(output); } } diff --git a/src/config/mod.rs b/src/config/mod.rs index 48c49931..3637f5c4 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -3,10 +3,13 @@ use crate::{ shell::Shell, state::{BackendData, State}, - wayland::protocols::output_configuration::OutputConfigurationState, + wayland::protocols::{ + output_configuration::OutputConfigurationState, workspace::WorkspaceUpdateGuard, + }, }; use cosmic_config::{ConfigGet, CosmicConfigEntry}; use serde::{Deserialize, Serialize}; +use smithay::wayland::xdg_activation::XdgActivationState; pub use smithay::{ backend::input::KeyState, input::keyboard::{keysyms as KeySyms, Keysym, ModifiersState}, @@ -277,6 +280,8 @@ impl Config { backend: &mut BackendData, shell: &mut Shell, loop_handle: &LoopHandle<'_, State>, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, + xdg_activation_state: &XdgActivationState, ) { let outputs = output_state.outputs().collect::>(); let mut infos = outputs @@ -308,9 +313,14 @@ impl Config { .get::>() .unwrap() .borrow_mut() = output_config; - if let Err(err) = - backend.apply_config_for_output(&output, false, shell, loop_handle) - { + if let Err(err) = backend.apply_config_for_output( + &output, + false, + shell, + loop_handle, + workspace_state, + xdg_activation_state, + ) { warn!( ?err, "Failed to set new config for output {}.", @@ -339,9 +349,14 @@ impl Config { .get::>() .unwrap() .borrow_mut() = output_config; - if let Err(err) = - backend.apply_config_for_output(&output, false, shell, loop_handle) - { + if let Err(err) = backend.apply_config_for_output( + &output, + false, + shell, + loop_handle, + workspace_state, + xdg_activation_state, + ) { error!(?err, "Failed to reset config for output {}.", output.name()); } else { if enabled { @@ -357,9 +372,14 @@ 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, loop_handle) - { + if let Err(err) = backend.apply_config_for_output( + &output, + false, + shell, + loop_handle, + workspace_state, + xdg_activation_state, + ) { warn!( ?err, "Failed to set new config for output {}.", @@ -518,7 +538,15 @@ fn config_changed(config: cosmic_config::Config, keys: Vec, state: &mut match key.as_str() { "xkb_config" => { let value = get_config::(&config, "xkb_config"); - let seats = state.common.shell.seats.iter().cloned().collect::>(); + let seats = state + .common + .shell + .read() + .unwrap() + .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)) { @@ -547,27 +575,41 @@ fn config_changed(config: cosmic_config::Config, keys: Vec, state: &mut "workspaces" => { state.common.config.cosmic_conf.workspaces = get_config::(&config, "workspaces"); - state.common.shell.update_config(&state.common.config); + state.common.update_config(); } "autotile" => { let new = get_config::(&config, "autotile"); if new != state.common.config.cosmic_conf.autotile { state.common.config.cosmic_conf.autotile = new; - state.common.shell.update_autotile(new); + + let mut shell = state.common.shell.write().unwrap(); + let shell_ref = &mut *shell; + shell_ref.workspaces.update_autotile( + new, + &mut state.common.workspace_state.update(), + shell_ref.seats.iter(), + ); } } "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; - state.common.shell.update_autotile_behavior(new); + + let mut shell = state.common.shell.write().unwrap(); + let shell_ref = &mut *shell; + shell_ref.workspaces.update_autotile_behavior( + new, + &mut state.common.workspace_state.update(), + shell_ref.seats.iter(), + ); } } "active_hint" => { let new = get_config::(&config, "active_hint"); if new != state.common.config.cosmic_conf.active_hint { state.common.config.cosmic_conf.active_hint = new; - state.common.shell.update_config(&state.common.config); + state.common.update_config(); } } _ => {} diff --git a/src/input/mod.rs b/src/input/mod.rs index 57e5f9f6..9dd124c1 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - config::{Action, KeyModifiers, KeyPattern}, + config::{Action, Config, KeyModifiers, KeyPattern}, input::gestures::{GestureState, SwipeAction}, shell::{ focus::{ @@ -16,11 +16,13 @@ use crate::{ Direction, FocusResult, InvalidWorkspaceIndex, MoveResult, OverviewMode, ResizeDirection, ResizeMode, SeatExt, Trigger, WorkspaceDelta, }, - state::Common, utils::prelude::*, wayland::{ handlers::{screencopy::SessionHolder, xdg_activation::ActivationContext}, - protocols::screencopy::{BufferConstraints, CursorSession}, + protocols::{ + screencopy::{BufferConstraints, CursorSession}, + workspace::WorkspaceUpdateGuard, + }, }, }; use calloop::{timer::Timer, RegistrationToken}; @@ -141,7 +143,8 @@ impl State { use smithay::backend::input::Event; match event { InputEvent::DeviceAdded { device } => { - let seat = &mut self.common.shell.seats.last_active(); + let shell = self.common.shell.read().unwrap(); + let seat = shell.seats.last_active(); for cap in seat.devices().add_device(&device) { match cap { DeviceCapability::TabletTool => { @@ -160,7 +163,7 @@ impl State { } } InputEvent::DeviceRemoved { device } => { - for seat in &mut self.common.shell.seats.iter() { + for seat in &mut self.common.shell.read().unwrap().seats.iter() { let devices = seat.devices(); if devices.has_device(&device) { for cap in devices.remove_device(&device) { @@ -186,12 +189,22 @@ impl State { let loop_handle = self.common.event_loop_handle.clone(); - if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { - let userdata = seat.user_data(); - + let maybe_seat = self + .common + .shell + .read() + .unwrap() + .seats + .for_device(&event.device()) + .cloned(); + if let Some(seat) = maybe_seat { let current_output = seat.active_output(); - let workspace = self.common.shell.active_space_mut(¤t_output); - let shortcuts_inhibited = workspace + let shortcuts_inhibited = self + .common + .shell + .read() + .unwrap() + .active_space(¤t_output) .focus_stack .get(&seat) .last() @@ -222,20 +235,21 @@ impl State { time, |data, modifiers, handle| { // Leave move overview mode, if any modifier was released + let mut shell = data.common.shell.write().unwrap(); if let OverviewMode::Started(Trigger::KeyboardMove(action_modifiers), _) = - data.common.shell.overview_mode().0 + shell.overview_mode().0 { if (action_modifiers.ctrl && !modifiers.ctrl) || (action_modifiers.alt && !modifiers.alt) || (action_modifiers.logo && !modifiers.logo) || (action_modifiers.shift && !modifiers.shift) { - data.common.shell.set_overview_mode(None, data.common.event_loop_handle.clone()); + shell.set_overview_mode(None, data.common.event_loop_handle.clone()); } } // Leave swap overview mode, if any key was released if let OverviewMode::Started(Trigger::KeyboardSwap(action_pattern, old_descriptor), _) = - data.common.shell.overview_mode().0 + shell.overview_mode().0 { if (action_pattern.modifiers.ctrl && !modifiers.ctrl) || (action_pattern.modifiers.alt && !modifiers.alt) @@ -243,16 +257,16 @@ impl State { || (action_pattern.modifiers.shift && !modifiers.shift) || (action_pattern.key.is_some() && handle.raw_syms().contains(&action_pattern.key.unwrap()) && state == KeyState::Released) { - data.common.shell.set_overview_mode(None, data.common.event_loop_handle.clone()); + shell.set_overview_mode(None, data.common.event_loop_handle.clone()); if let Some(focus) = current_focus { - if let Some(new_descriptor) = data.common.shell.workspaces.active(¤t_output).1.node_desc(focus) { - let mut spaces = data.common.shell.workspaces.spaces_mut(); + if let Some(new_descriptor) = shell.workspaces.active(¤t_output).1.node_desc(focus) { + let mut spaces = shell.workspaces.spaces_mut(); if old_descriptor.handle != new_descriptor.handle { let (mut old_w, mut other_w) = spaces.partition::, _>(|w| w.handle == old_descriptor.handle); if let Some(old_workspace) = old_w.get_mut(0) { if let Some(new_workspace) = other_w.iter_mut().find(|w| w.handle == new_descriptor.handle) { - 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) { + if let Some(focus) = TilingLayout::swap_trees(&mut old_workspace.tiling_layer, Some(&mut new_workspace.tiling_layer), &old_descriptor, &new_descriptor) { let seat = seat.clone(); data.common.event_loop_handle.insert_idle(move |state| { Shell::set_focus(state, Some(&focus), &seat, None); @@ -264,7 +278,7 @@ impl State { } } else { if let Some(workspace) = spaces.find(|w| w.handle == new_descriptor.handle) { - if let Some(focus) = TilingLayout::swap_trees(&mut workspace.tiling_layer, None, &old_descriptor, &new_descriptor, &mut data.common.shell.toplevel_info_state) { + if let Some(focus) = TilingLayout::swap_trees(&mut workspace.tiling_layer, None, &old_descriptor, &new_descriptor) { std::mem::drop(spaces); let seat = seat.clone(); data.common.event_loop_handle.insert_idle(move |state| { @@ -276,14 +290,14 @@ impl State { } } } else { - let new_workspace = data.common.shell.workspaces.active(¤t_output).1.handle; + let new_workspace = shell.workspaces.active(¤t_output).1.handle; if new_workspace != old_descriptor.handle { - let spaces = data.common.shell.workspaces.spaces_mut(); + let spaces = shell.workspaces.spaces_mut(); let (mut old_w, mut other_w) = spaces.partition::, _>(|w| w.handle == old_descriptor.handle); if let Some(old_workspace) = old_w.get_mut(0) { if let Some(new_workspace) = other_w.iter_mut().find(|w| w.handle == new_workspace) { if new_workspace.tiling_layer.windows().next().is_none() { - 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) { + 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) { let seat = seat.clone(); data.common.event_loop_handle.insert_idle(move |state| { Shell::set_focus(state, Some(&focus), &seat, None); @@ -300,12 +314,12 @@ impl State { // Leave or update resize mode, if modifiers changed or initial key was released if let (ResizeMode::Started(action_pattern, _, _), _) = - data.common.shell.resize_mode() + shell.resize_mode() { if action_pattern.key.is_some() && state == KeyState::Released && handle.raw_syms().contains(&action_pattern.key.unwrap()) { - data.common.shell.set_resize_mode(None, &data.common.config, data.common.event_loop_handle.clone()); + shell.set_resize_mode(None, &data.common.config, data.common.event_loop_handle.clone()); } else if action_pattern.modifiers != *modifiers { let mut new_pattern = action_pattern.clone(); new_pattern.modifiers = modifiers.clone().into(); @@ -325,13 +339,13 @@ impl State { None } }); - data.common.shell.set_resize_mode(enabled, &data.common.config, data.common.event_loop_handle.clone()); + shell.set_resize_mode(enabled, &data.common.config, data.common.event_loop_handle.clone()); } } // Special case resizing with regards to arrow keys if let (ResizeMode::Started(_, _, direction), _) = - data.common.shell.resize_mode() + shell.resize_mode() { let resize_edge = match handle.modified_sym() { Keysym::Left | Keysym::h | Keysym::H => Some(ResizeEdge::LEFT), @@ -352,7 +366,7 @@ impl State { }; if state == KeyState::Released { - if let Some(tokens) = userdata.get::().unwrap().filter(&handle) { + if let Some(tokens) = seat.supressed_keys().filter(&handle) { for token in tokens { loop_handle.remove(token); } @@ -370,9 +384,7 @@ impl State { }).ok() } else { None }; - userdata - .get::() - .unwrap() + seat.supressed_keys() .add(&handle, token); } return FilterResult::Intercept(Some(( @@ -391,9 +403,7 @@ impl State { && !modifiers.logo && !modifiers.shift { - userdata - .get::() - .unwrap() + seat.supressed_keys() .add(&handle, None); return FilterResult::Intercept(Some(( Action::Escape, @@ -406,7 +416,7 @@ impl State { // Skip released events for initially surpressed keys if state == KeyState::Released { - if let Some(tokens) = userdata.get::().unwrap().filter(&handle) { + if let Some(tokens) = seat.supressed_keys().filter(&handle) { for token in tokens { loop_handle.remove(token); } @@ -417,7 +427,7 @@ impl State { // Pass keys to debug interface, if it has focus #[cfg(feature = "debug")] { - if data.common.seats().position(|x| x == &seat).unwrap() == 0 + if shell.seats.iter().position(|x| x == &seat).unwrap() == 0 && data.common.egui.active { if data.common.egui.state.wants_keyboard() { @@ -426,9 +436,7 @@ impl State { state == KeyState::Pressed, modifiers.clone(), ); - userdata - .get::() - .unwrap() + seat.supressed_keys() .add(&handle, None); return FilterResult::Intercept(None); } @@ -447,14 +455,14 @@ impl State { ) { error!(?err, "Failed switching virtual terminal."); } - userdata.get::().unwrap().add(&handle, None); + seat.supressed_keys().add(&handle, None); return FilterResult::Intercept(None); } // handle the rest of the global shortcuts let mut can_clear_modifiers_shortcut = true; if !shortcuts_inhibited { - let modifiers_queue = userdata.get::().unwrap(); + let modifiers_queue = seat.modifiers_shortcut_queue(); for (binding, action) in data.common.config.static_conf.key_bindings.iter() { @@ -476,10 +484,7 @@ impl State { ) || modifiers_bypass { modifiers_queue.clear(); - userdata - .get::() - .unwrap() - .add(&handle, None); + seat.supressed_keys().add(&handle, None); return FilterResult::Intercept(Some(( action.clone(), binding.clone(), @@ -489,7 +494,7 @@ impl State { } if can_clear_modifiers_shortcut { - userdata.get::().unwrap().clear(); + seat.modifiers_shortcut_queue().clear(); } // keys are passed through to apps @@ -505,14 +510,14 @@ impl State { InputEvent::PointerMotion { event, .. } => { use smithay::backend::input::PointerMotionEvent; - if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { + let mut shell = self.common.shell.write().unwrap(); + if let Some(seat) = shell.seats.for_device(&event.device()).cloned() { let current_output = seat.active_output(); let mut position = seat.get_pointer().unwrap().current_location().as_global(); - let under = - State::surface_under(position, ¤t_output, &mut self.common.shell) - .map(|(target, pos)| (target, pos.as_logical())); + let under = State::surface_under(position, ¤t_output, &mut *shell) + .map(|(target, pos)| (target, pos.as_logical())); let ptr = seat.get_pointer().unwrap(); @@ -545,6 +550,18 @@ impl State { }); } + position += event.delta().as_global(); + + let output = shell + .outputs() + .find(|output| output.geometry().to_f64().contains(position)) + .cloned() + .unwrap_or(current_output.clone()); + + let new_under = State::surface_under(position, &output, &mut *shell) + .map(|(target, pos)| (target, pos.as_logical())); + + std::mem::drop(shell); ptr.relative_motion( self, under.clone(), @@ -560,16 +577,6 @@ impl State { return; } - position += event.delta().as_global(); - - let output = self - .common - .shell - .outputs() - .find(|output| output.geometry().to_f64().contains(position)) - .cloned() - .unwrap_or(current_output.clone()); - if ptr.is_grabbed() && seat .user_data() @@ -585,9 +592,6 @@ impl State { let output_geometry = output.geometry(); - let new_under = State::surface_under(position, &output, &mut self.common.shell) - .map(|(target, pos)| (target, pos.as_logical())); - position.x = position.x.clamp( output_geometry.loc.x as f64, ((output_geometry.loc.x + output_geometry.size.w) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable @@ -662,14 +666,16 @@ impl State { }); } + let shell = self.common.shell.read().unwrap(); + if output != current_output { - for session in cursor_sessions_for_output(&self.common, ¤t_output) { + for session in cursor_sessions_for_output(&*shell, ¤t_output) { session.set_cursor_pos(None); } seat.set_active_output(&output); } - for session in cursor_sessions_for_output(&self.common, &output) { + for session in cursor_sessions_for_output(&shell, &output) { if let Some((geometry, offset)) = seat.cursor_geometry( position.as_logical().to_buffer( output.current_scale().fractional_scale(), @@ -694,8 +700,8 @@ impl State { } } #[cfg(feature = "debug")] - if self.common.seats().position(|x| x == &seat).unwrap() == 0 { - if let Some(output) = self.common.shell.outputs().next() { + if shell.seats().position(|x| x == &seat).unwrap() == 0 { + if let Some(output) = shell.outputs().next() { let location = position.to_local(&output).to_i32_round().as_logical(); self.common.egui.state.handle_pointer_motion(location); } @@ -703,7 +709,15 @@ impl State { } } InputEvent::PointerMotionAbsolute { event, .. } => { - if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { + let maybe_seat = self + .common + .shell + .read() + .unwrap() + .seats + .for_device(&event.device()) + .cloned(); + if let Some(seat) = maybe_seat { let output = seat.active_output(); let geometry = output.geometry(); let position = geometry.loc.to_f64() @@ -713,10 +727,27 @@ impl State { ) .as_global(); let serial = SERIAL_COUNTER.next_serial(); - let under = State::surface_under(position, &output, &mut self.common.shell) - .map(|(target, pos)| (target, pos.as_logical())); + let under = State::surface_under( + position, + &output, + &mut *self.common.shell.write().unwrap(), + ) + .map(|(target, pos)| (target, pos.as_logical())); + + let ptr = seat.get_pointer().unwrap(); + ptr.motion( + self, + under, + &MotionEvent { + location: position.as_logical(), + serial, + time: event.time_msec(), + }, + ); + ptr.frame(self); - for session in cursor_sessions_for_output(&self.common, &output) { + let shell = self.common.shell.read().unwrap(); + for session in cursor_sessions_for_output(&*shell, &output) { if let Some((geometry, offset)) = seat.cursor_geometry( position.as_logical().to_buffer( output.current_scale().fractional_scale(), @@ -740,20 +771,9 @@ impl State { session.set_cursor_pos(Some(geometry.loc)); } } - let ptr = seat.get_pointer().unwrap(); - ptr.motion( - self, - under, - &MotionEvent { - location: position.as_logical(), - serial, - time: event.time_msec(), - }, - ); - ptr.frame(self); #[cfg(feature = "debug")] - if self.common.seats().position(|x| x == &seat).unwrap() == 0 { - if let Some(output) = self.common.shell.outputs().next() { + if shell.seats.iter().position(|x| x == &seat).unwrap() == 0 { + if let Some(output) = shell.outputs().next() { let location = position.to_local(&output).to_i32_round().as_logical(); self.common.egui.state.handle_pointer_motion(location); } @@ -763,9 +783,10 @@ impl State { InputEvent::PointerButton { event, .. } => { use smithay::backend::input::{ButtonState, PointerButtonEvent}; - if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { + let mut shell = self.common.shell.write().unwrap(); + if let Some(seat) = shell.seats.for_device(&event.device()).cloned() { #[cfg(feature = "debug")] - if self.common.seats().position(|x| x == &seat).unwrap() == 0 + if shell.seats.iter().position(|x| x == &seat).unwrap() == 0 && self.common.egui.active { if self.common.egui.state.wants_pointer() { @@ -793,13 +814,13 @@ impl State { let relative_pos = pos.to_local(&output); let mut under: Option = None; - if let Some(session_lock) = self.common.shell.session_lock.as_ref() { + if let Some(session_lock) = shell.session_lock.as_ref() { under = session_lock .surfaces .get(&output) .map(|lock| lock.clone().into()); } else if let Some(window) = - self.common.shell.active_space(&output).get_fullscreen() + shell.active_space(&output).get_fullscreen() { let layers = layer_map_for_output(&output); if let Some(layer) = @@ -852,22 +873,43 @@ impl State { if !done { // Don't check override redirect windows, because we don't set keyboard focus to them explicitly. // These cases are handled by the XwaylandKeyboardGrab. - if let Some(target) = - self.common.shell.element_under(pos, &output) - { + if let Some(target) = shell.element_under(pos, &output) { if seat.get_keyboard().unwrap().modifier_state().logo { if let Some(surface) = target.toplevel() { let seat_clone = seat.clone(); self.common.event_loop_handle.insert_idle( move |state| { - Shell::move_request( - state, + let mut shell = + state.common.shell.write().unwrap(); + let res = shell.move_request( &surface, &seat_clone, serial, ReleaseMode::NoMouseButtons, false, - ) + &state.common.config, + &state.common.event_loop_handle, + &state.common.xdg_activation_state, + ); + if let Some((target, focus)) = res { + std::mem::drop(shell); + if target.is_touch_grab() { + seat_clone + .get_touch() + .unwrap() + .set_grab( + state, target, serial, + ); + } else { + seat_clone + .get_pointer() + .unwrap() + .set_grab( + state, target, serial, + focus, + ); + } + } }, ); } @@ -905,19 +947,23 @@ impl State { } } } + std::mem::drop(shell); Shell::set_focus(self, under.as_ref(), &seat, Some(serial)); + } else { + std::mem::drop(shell); } } else { if let OverviewMode::Started(Trigger::Pointer(action_button), _) = - self.common.shell.overview_mode().0 + shell.overview_mode().0 { if action_button == button { - self.common - .shell + shell .set_overview_mode(None, self.common.event_loop_handle.clone()); } } + std::mem::drop(shell); }; + let ptr = seat.get_pointer().unwrap(); ptr.button( self, @@ -939,9 +985,26 @@ impl State { 1.0 }; - if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { + let maybe_seat = self + .common + .shell + .read() + .unwrap() + .seats + .for_device(&event.device()) + .cloned(); + if let Some(seat) = maybe_seat { #[cfg(feature = "debug")] - if self.common.seats().position(|x| x == seat).unwrap() == 0 + if self + .common + .shell + .read() + .unwrap() + .seats + .iter() + .position(|x| x == &seat) + .unwrap() + == 0 && self.common.egui.active { if self.common.egui.state.wants_pointer() { @@ -993,7 +1056,15 @@ impl State { } } InputEvent::GestureSwipeBegin { event, .. } => { - if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { + let maybe_seat = self + .common + .shell + .read() + .unwrap() + .seats + .for_device(&event.device()) + .cloned(); + if let Some(seat) = maybe_seat { if event.fingers() >= 3 { self.common.gesture_state = Some(GestureState::new(event.fingers())); } else { @@ -1011,7 +1082,15 @@ impl State { } } InputEvent::GestureSwipeUpdate { event, .. } => { - if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { + let maybe_seat = self + .common + .shell + .read() + .unwrap() + .seats + .for_device(&event.device()) + .cloned(); + if let Some(seat) = maybe_seat { let mut activate_action: Option = None; if let Some(ref mut gesture_state) = self.common.gesture_state { let first_update = gesture_state.update( @@ -1053,7 +1132,7 @@ impl State { match gesture_state.action { Some(SwipeAction::NextWorkspace) | Some(SwipeAction::PrevWorkspace) => { - self.common.shell.update_workspace_delta( + self.common.shell.write().unwrap().update_workspace_delta( &seat.active_output(), gesture_state.delta, ) @@ -1072,17 +1151,35 @@ impl State { } match activate_action { Some(SwipeAction::NextWorkspace) => { - let _ = self.to_next_workspace(&seat, true); + let _ = to_next_workspace( + &mut *self.common.shell.write().unwrap(), + &seat, + true, + &mut self.common.workspace_state.update(), + ); } Some(SwipeAction::PrevWorkspace) => { - let _ = self.to_previous_workspace(&seat, true); + let _ = to_previous_workspace( + &mut *self.common.shell.write().unwrap(), + &seat, + true, + &mut self.common.workspace_state.update(), + ); } _ => {} } } } InputEvent::GestureSwipeEnd { event, .. } => { - if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { + let maybe_seat = self + .common + .shell + .read() + .unwrap() + .seats + .for_device(&event.device()) + .cloned(); + if let Some(seat) = maybe_seat { if let Some(ref gesture_state) = self.common.gesture_state { match gesture_state.action { Some(SwipeAction::NextWorkspace) | Some(SwipeAction::PrevWorkspace) => { @@ -1095,10 +1192,11 @@ impl State { } else { velocity / seat.active_output().geometry().size.h as f64 }; - let _ = self - .common - .shell - .end_workspace_swipe(&seat.active_output(), norm_velocity); + let _ = self.common.shell.write().unwrap().end_workspace_swipe( + &seat.active_output(), + norm_velocity, + &mut self.common.workspace_state.update(), + ); } _ => {} } @@ -1118,7 +1216,15 @@ impl State { } } InputEvent::GesturePinchBegin { event, .. } => { - if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { + let maybe_seat = self + .common + .shell + .read() + .unwrap() + .seats + .for_device(&event.device()) + .cloned(); + if let Some(seat) = maybe_seat { let serial = SERIAL_COUNTER.next_serial(); let pointer = seat.get_pointer().unwrap(); pointer.gesture_pinch_begin( @@ -1132,7 +1238,15 @@ impl State { } } InputEvent::GesturePinchUpdate { event, .. } => { - if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { + let maybe_seat = self + .common + .shell + .read() + .unwrap() + .seats + .for_device(&event.device()) + .cloned(); + if let Some(seat) = maybe_seat { let pointer = seat.get_pointer().unwrap(); pointer.gesture_pinch_update( self, @@ -1146,7 +1260,15 @@ impl State { } } InputEvent::GesturePinchEnd { event, .. } => { - if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { + let maybe_seat = self + .common + .shell + .read() + .unwrap() + .seats + .for_device(&event.device()) + .cloned(); + if let Some(seat) = maybe_seat { let serial = SERIAL_COUNTER.next_serial(); let pointer = seat.get_pointer().unwrap(); pointer.gesture_pinch_end( @@ -1160,7 +1282,15 @@ impl State { } } InputEvent::GestureHoldBegin { event, .. } => { - if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { + let maybe_seat = self + .common + .shell + .read() + .unwrap() + .seats + .for_device(&event.device()) + .cloned(); + if let Some(seat) = maybe_seat { let serial = SERIAL_COUNTER.next_serial(); let pointer = seat.get_pointer().unwrap(); pointer.gesture_hold_begin( @@ -1174,7 +1304,15 @@ impl State { } } InputEvent::GestureHoldEnd { event, .. } => { - if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { + let maybe_seat = self + .common + .shell + .read() + .unwrap() + .seats + .for_device(&event.device()) + .cloned(); + if let Some(seat) = maybe_seat { let serial = SERIAL_COUNTER.next_serial(); let pointer = seat.get_pointer().unwrap(); pointer.gesture_hold_end( @@ -1188,9 +1326,11 @@ impl State { } } InputEvent::TouchDown { event, .. } => { - if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { + let mut shell = self.common.shell.write().unwrap(); + if let Some(seat) = shell.seats.for_device(&event.device()).cloned() { let Some(output) = - mapped_output_for_device(&self.common, &event.device()).cloned() + mapped_output_for_device(&self.common.config, &*shell, &event.device()) + .cloned() else { return; }; @@ -1202,9 +1342,11 @@ impl State { .position_transformed(geometry.size.as_logical()) .as_global(); - let under = State::surface_under(position, &output, &mut self.common.shell) + let under = State::surface_under(position, &output, &mut *shell) .map(|(target, pos)| (target, pos.as_logical())); + std::mem::drop(shell); + let serial = SERIAL_COUNTER.next_serial(); let touch = seat.get_touch().unwrap(); touch.down( @@ -1220,9 +1362,11 @@ impl State { } } InputEvent::TouchMotion { event, .. } => { - if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { + let mut shell = self.common.shell.write().unwrap(); + if let Some(seat) = shell.seats.for_device(&event.device()).cloned() { let Some(output) = - mapped_output_for_device(&self.common, &event.device()).cloned() + mapped_output_for_device(&self.common.config, &*shell, &event.device()) + .cloned() else { return; }; @@ -1234,9 +1378,11 @@ impl State { .position_transformed(geometry.size.as_logical()) .as_global(); - let under = State::surface_under(position, &output, &mut self.common.shell) + let under = State::surface_under(position, &output, &mut *shell) .map(|(target, pos)| (target, pos.as_logical())); + std::mem::drop(shell); + let touch = seat.get_touch().unwrap(); touch.motion( self, @@ -1250,17 +1396,16 @@ impl State { } } InputEvent::TouchUp { event, .. } => { - if let OverviewMode::Started(Trigger::Touch(slot), _) = - self.common.shell.overview_mode().0 - { + let mut shell = self.common.shell.write().unwrap(); + if let OverviewMode::Started(Trigger::Touch(slot), _) = shell.overview_mode().0 { if slot == event.slot() { - self.common - .shell - .set_overview_mode(None, self.common.event_loop_handle.clone()); + shell.set_overview_mode(None, self.common.event_loop_handle.clone()); } } - if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { + let maybe_seat = shell.seats.for_device(&event.device()).cloned(); + if let Some(seat) = maybe_seat { + std::mem::drop(shell); let serial = SERIAL_COUNTER.next_serial(); let touch = seat.get_touch().unwrap(); touch.up( @@ -1274,21 +1419,39 @@ impl State { } } InputEvent::TouchCancel { event, .. } => { - if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { + let maybe_seat = self + .common + .shell + .read() + .unwrap() + .seats + .for_device(&event.device()) + .cloned(); + if let Some(seat) = maybe_seat { let touch = seat.get_touch().unwrap(); touch.cancel(self); } } InputEvent::TouchFrame { event, .. } => { - if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { + let maybe_seat = self + .common + .shell + .read() + .unwrap() + .seats + .for_device(&event.device()) + .cloned(); + if let Some(seat) = maybe_seat { let touch = seat.get_touch().unwrap(); touch.frame(self); } } InputEvent::TabletToolAxis { event, .. } => { - if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { + let mut shell = self.common.shell.write().unwrap(); + if let Some(seat) = shell.seats.for_device(&event.device()).cloned() { let Some(output) = - mapped_output_for_device(&self.common, &event.device()).cloned() + mapped_output_for_device(&self.common.config, &shell, &event.device()) + .cloned() else { return; }; @@ -1299,9 +1462,11 @@ impl State { .as_global() + geometry.loc.to_f64(); - let under = State::surface_under(position, &output, &mut self.common.shell) + let under = State::surface_under(position, &output, &mut *shell) .map(|(target, pos)| (target, pos.as_logical())); + std::mem::drop(shell); + let pointer = seat.get_pointer().unwrap(); pointer.motion( self, @@ -1349,9 +1514,11 @@ impl State { } } InputEvent::TabletToolProximity { event, .. } => { - if let Some(seat) = self.common.shell.seats.for_device(&event.device()).cloned() { + let mut shell = self.common.shell.write().unwrap(); + if let Some(seat) = shell.seats.for_device(&event.device()).cloned() { let Some(output) = - mapped_output_for_device(&self.common, &event.device()).cloned() + mapped_output_for_device(&self.common.config, &shell, &event.device()) + .cloned() else { return; }; @@ -1362,9 +1529,11 @@ impl State { .as_global() + geometry.loc.to_f64(); - let under = State::surface_under(position, &output, &mut self.common.shell) + let under = State::surface_under(position, &output, &mut *shell) .map(|(target, pos)| (target, pos.as_logical())); + std::mem::drop(shell); + let pointer = seat.get_pointer().unwrap(); pointer.motion( self, @@ -1402,7 +1571,15 @@ impl State { } } InputEvent::TabletToolTip { event, .. } => { - if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { + let maybe_seat = self + .common + .shell + .read() + .unwrap() + .seats + .for_device(&event.device()) + .cloned(); + if let Some(seat) = maybe_seat { if let Some(tool) = seat.tablet_seat().get_tool(&event.tool()) { match event.tip_state() { TabletToolTipState::Down => { @@ -1416,7 +1593,15 @@ impl State { } } InputEvent::TabletToolButton { event, .. } => { - if let Some(seat) = self.common.shell.seats.for_device(&event.device()) { + let maybe_seat = self + .common + .shell + .read() + .unwrap() + .seats + .for_device(&event.device()) + .cloned(); + if let Some(seat) = maybe_seat { if let Some(tool) = seat.tablet_seat().get_tool(&event.tool()) { tool.button( event.button(), @@ -1444,7 +1629,7 @@ impl State { ) { // TODO: Detect if started from login manager or tty, and only allow // `Terminate` if it will return to login manager. - if self.common.shell.session_lock.is_some() + if self.common.shell.read().unwrap().session_lock.is_some() && !matches!(action, Action::Terminate | Action::Debug) { return; @@ -1460,6 +1645,8 @@ impl State { for mapped in self .common .shell + .read() + .unwrap() .workspaces .spaces() .flat_map(|w| w.mapped()) @@ -1473,20 +1660,22 @@ impl State { } Action::Close => { let current_output = seat.active_output(); - let workspace = self.common.shell.active_space_mut(¤t_output); + let shell = self.common.shell.read().unwrap(); + let workspace = shell.active_space(¤t_output); if let Some(window) = workspace.focus_stack.get(seat).last() { window.send_close(); } } Action::Escape => { - self.common - .shell - .set_overview_mode(None, self.common.event_loop_handle.clone()); - self.common.shell.set_resize_mode( - None, - &self.common.config, - self.common.event_loop_handle.clone(), - ); + { + let mut shell = self.common.shell.write().unwrap(); + shell.set_overview_mode(None, self.common.event_loop_handle.clone()); + shell.set_resize_mode( + None, + &self.common.config, + self.common.event_loop_handle.clone(), + ); + } let pointer = seat.get_pointer().unwrap(); let keyboard = seat.get_keyboard().unwrap(); if pointer.is_grabbed() { @@ -1502,28 +1691,32 @@ impl State { 0 => 9, x => x - 1, }; - let _ = self.common.shell.activate( + let _ = self.common.shell.write().unwrap().activate( ¤t_output, workspace as usize, WorkspaceDelta::new_shortcut(), + &mut self.common.workspace_state.update(), ); } Action::LastWorkspace => { let current_output = seat.active_output(); - let workspace = self - .common - .shell - .workspaces - .len(¤t_output) - .saturating_sub(1); - let _ = self.common.shell.activate( + let mut shell = self.common.shell.write().unwrap(); + let workspace = shell.workspaces.len(¤t_output).saturating_sub(1); + let _ = shell.activate( ¤t_output, workspace, WorkspaceDelta::new_shortcut(), + &mut self.common.workspace_state.update(), ); } Action::NextWorkspace => { - if self.to_next_workspace(seat, false).is_err() && propagate { + let next = to_next_workspace( + &mut *self.common.shell.write().unwrap(), + seat, + false, + &mut self.common.workspace_state.update(), + ); + if next.is_err() && propagate { if let Some(inferred) = pattern.inferred_direction() { self.handle_action( Action::SwitchOutput(inferred), @@ -1538,7 +1731,13 @@ impl State { } } Action::PreviousWorkspace => { - if self.to_previous_workspace(seat, false).is_err() && propagate { + let previous = to_previous_workspace( + &mut *self.common.shell.write().unwrap(), + seat, + false, + &mut self.common.workspace_state.update(), + ); + if previous.is_err() && propagate { if let Some(inferred) = pattern.inferred_direction() { self.handle_action( Action::SwitchOutput(inferred), @@ -1560,50 +1759,55 @@ impl State { Action::MoveToWorkspace(x) | Action::SendToWorkspace(x) => x - 1, _ => unreachable!(), }; - if let Ok(Some((target, _point))) = self.common.shell.move_current_window( + let res = self.common.shell.write().unwrap().move_current_window( seat, ¤t_output, (¤t_output, Some(workspace as usize)), follow, None, - ) { + &mut self.common.workspace_state.update(), + ); + if let Ok(Some((target, _point))) = res { Shell::set_focus(self, Some(&target), seat, None); } } x @ Action::MoveToLastWorkspace | x @ Action::SendToLastWorkspace => { let current_output = seat.active_output(); - let workspace = self - .common - .shell - .workspaces - .len(¤t_output) - .saturating_sub(1); - if let Ok(Some((target, _point))) = self.common.shell.move_current_window( + let mut shell = self.common.shell.write().unwrap(); + let workspace = shell.workspaces.len(¤t_output).saturating_sub(1); + let res = shell.move_current_window( seat, ¤t_output, (¤t_output, Some(workspace as usize)), matches!(x, Action::MoveToLastWorkspace), None, - ) { + &mut self.common.workspace_state.update(), + ); + if let Ok(Some((target, _point))) = res { + std::mem::drop(shell); Shell::set_focus(self, Some(&target), seat, None); } } x @ Action::MoveToNextWorkspace | x @ Action::SendToNextWorkspace => { let current_output = seat.active_output(); - let workspace = self - .common - .shell - .workspaces - .active_num(¤t_output) - .1 - .saturating_add(1); - match self.common.shell.move_current_window( - seat, - ¤t_output, - (¤t_output, Some(workspace as usize)), - matches!(x, Action::MoveToNextWorkspace), - direction, - ) { + let res = { + let mut shell = self.common.shell.write().unwrap(); + let workspace = shell + .workspaces + .active_num(¤t_output) + .1 + .saturating_add(1); + shell.move_current_window( + seat, + ¤t_output, + (¤t_output, Some(workspace as usize)), + matches!(x, Action::MoveToNextWorkspace), + direction, + &mut self.common.workspace_state.update(), + ) + }; + + match res { Ok(Some((target, _point))) => { Shell::set_focus(self, Some(&target), seat, None); } @@ -1629,21 +1833,25 @@ impl State { } x @ Action::MoveToPreviousWorkspace | x @ Action::SendToPreviousWorkspace => { let current_output = seat.active_output(); - let workspace = self - .common - .shell - .workspaces - .active_num(¤t_output) - .1 - .saturating_sub(1); - // TODO: Possibly move to prev output, if idx < 0 - match self.common.shell.move_current_window( - seat, - ¤t_output, - (¤t_output, Some(workspace as usize)), - matches!(x, Action::MoveToPreviousWorkspace), - direction, - ) { + let res = { + let mut shell = self.common.shell.write().unwrap(); + let workspace = shell + .workspaces + .active_num(¤t_output) + .1 + .saturating_sub(1); + // TODO: Possibly move to prev output, if idx < 0 + shell.move_current_window( + seat, + ¤t_output, + (¤t_output, Some(workspace as usize)), + matches!(x, Action::MoveToPreviousWorkspace), + direction, + &mut self.common.workspace_state.update(), + ) + }; + + match res { Ok(Some((target, _point))) => { Shell::set_focus(self, Some(&target), seat, None); } @@ -1669,20 +1877,21 @@ impl State { } Action::SwitchOutput(direction) => { let current_output = seat.active_output(); - let next_output = self - .common - .shell - .next_output(¤t_output, direction) - .cloned(); + let mut shell = self.common.shell.write().unwrap(); + + let next_output = shell.next_output(¤t_output, direction).cloned(); if let Some(next_output) = next_output { - let idx = self.common.shell.workspaces.active_num(&next_output).1; - match self.common.shell.activate( + let idx = shell.workspaces.active_num(&next_output).1; + let res = shell.activate( &next_output, idx, WorkspaceDelta::new_shortcut(), - ) { + &mut self.common.workspace_state.update(), + ); + match res { Ok(Some(new_pos)) => { + std::mem::drop(shell); seat.set_active_output(&next_output); if let Some(ptr) = seat.get_pointer() { ptr.motion( @@ -1703,6 +1912,7 @@ impl State { _ => {} } } else if propagate { + std::mem::drop(shell); match ( direction, self.common.config.cosmic_conf.workspaces.workspace_layout, @@ -1734,22 +1944,25 @@ impl State { } Action::NextOutput => { let current_output = seat.active_output(); - let next_output = self - .common - .shell + let mut shell = self.common.shell.write().unwrap(); + + let next_output = shell .outputs() .skip_while(|o| *o != ¤t_output) .skip(1) .next() .cloned(); if let Some(next_output) = next_output { - let idx = self.common.shell.workspaces.active_num(&next_output).1; - match self.common.shell.activate( + let idx = shell.workspaces.active_num(&next_output).1; + let res = shell.activate( &next_output, idx, WorkspaceDelta::new_shortcut(), - ) { + &mut self.common.workspace_state.update(), + ); + match res { Ok(Some(new_pos)) => { + std::mem::drop(shell); seat.set_active_output(&next_output); if let Some(ptr) = seat.get_pointer() { ptr.motion( @@ -1773,9 +1986,9 @@ impl State { } Action::PreviousOutput => { let current_output = seat.active_output(); - let prev_output = self - .common - .shell + let mut shell = self.common.shell.write().unwrap(); + + let prev_output = shell .outputs() .rev() .skip_while(|o| *o != ¤t_output) @@ -1783,13 +1996,16 @@ impl State { .next() .cloned(); if let Some(prev_output) = prev_output { - let idx = self.common.shell.workspaces.active_num(&prev_output).1; - match self.common.shell.activate( + let idx = shell.workspaces.active_num(&prev_output).1; + let res = shell.activate( &prev_output, idx, WorkspaceDelta::new_shortcut(), - ) { + &mut self.common.workspace_state.update(), + ); + match res { Ok(Some(new_pos)) => { + std::mem::drop(shell); seat.set_active_output(&prev_output); if let Some(ptr) = seat.get_pointer() { ptr.motion( @@ -1820,20 +2036,20 @@ impl State { }; let current_output = seat.active_output(); - let next_output = self - .common - .shell - .next_output(¤t_output, direction) - .cloned(); + let mut shell = self.common.shell.write().unwrap(); + let next_output = shell.next_output(¤t_output, direction).cloned(); if let Some(next_output) = next_output { - if let Ok(Some((target, new_pos))) = self.common.shell.move_current_window( + let res = shell.move_current_window( seat, ¤t_output, (&next_output, None), is_move_action, Some(direction), - ) { + &mut self.common.workspace_state.update(), + ); + if let Ok(Some((target, new_pos))) = res { + std::mem::drop(shell); Shell::set_focus(self, Some(&target), seat, None); if let Some(ptr) = seat.get_pointer() { ptr.motion( @@ -1849,6 +2065,7 @@ impl State { } } } else if propagate { + std::mem::drop(shell); match ( direction, self.common.config.cosmic_conf.workspaces.workspace_layout, @@ -1880,22 +2097,25 @@ impl State { } x @ Action::MoveToNextOutput | x @ Action::SendToNextOutput => { let current_output = seat.active_output(); - let next_output = self - .common - .shell + let mut shell = self.common.shell.write().unwrap(); + + let next_output = shell .outputs() .skip_while(|o| *o != ¤t_output) .skip(1) .next() .cloned(); if let Some(next_output) = next_output { - if let Ok(Some((target, new_pos))) = self.common.shell.move_current_window( + let res = shell.move_current_window( seat, ¤t_output, (&next_output, None), matches!(x, Action::MoveToNextOutput), direction, - ) { + &mut self.common.workspace_state.update(), + ); + if let Ok(Some((target, new_pos))) = res { + std::mem::drop(shell); Shell::set_focus(self, Some(&target), seat, None); if let Some(ptr) = seat.get_pointer() { ptr.motion( @@ -1914,9 +2134,9 @@ impl State { } x @ Action::MoveToPreviousOutput | x @ Action::SendToPreviousOutput => { let current_output = seat.active_output(); - let prev_output = self - .common - .shell + let mut shell = self.common.shell.write().unwrap(); + + let prev_output = shell .outputs() .rev() .skip_while(|o| *o != ¤t_output) @@ -1924,13 +2144,16 @@ impl State { .next() .cloned(); if let Some(prev_output) = prev_output { - if let Ok(Some((target, new_pos))) = self.common.shell.move_current_window( + let res = shell.move_current_window( seat, ¤t_output, (&prev_output, None), matches!(x, Action::MoveToPreviousOutput), direction, - ) { + &mut self.common.workspace_state.update(), + ); + if let Ok(Some((target, new_pos))) = res { + std::mem::drop(shell); Shell::set_focus(self, Some(&target), seat, None); if let Some(ptr) = seat.get_pointer() { ptr.motion( @@ -1949,56 +2172,59 @@ impl State { } Action::MigrateWorkspaceToNextOutput => { let current_output = seat.active_output(); - let active = self.common.shell.active_space(¤t_output).handle; - let next_output = self - .common - .shell - .outputs() - .skip_while(|o| *o != ¤t_output) - .skip(1) - .next() - .cloned(); + let (active, next_output) = { + let shell = self.common.shell.read().unwrap(); + let output = shell + .outputs() + .skip_while(|o| *o != ¤t_output) + .skip(1) + .next() + .cloned(); + + (shell.active_space(¤t_output).handle, output) + }; if let Some(next_output) = next_output { self.common - .shell .migrate_workspace(¤t_output, &next_output, &active); } } Action::MigrateWorkspaceToPreviousOutput => { let current_output = seat.active_output(); - let active = self.common.shell.active_space(¤t_output).handle; - let prev_output = self - .common - .shell - .outputs() - .rev() - .skip_while(|o| *o != ¤t_output) - .skip(1) - .next() - .cloned(); + let (active, prev_output) = { + let shell = self.common.shell.read().unwrap(); + let output = shell + .outputs() + .rev() + .skip_while(|o| *o != ¤t_output) + .skip(1) + .next() + .cloned(); + + (shell.active_space(¤t_output).handle, output) + }; if let Some(prev_output) = prev_output { self.common - .shell .migrate_workspace(¤t_output, &prev_output, &active); } } Action::MigrateWorkspaceToOutput(direction) => { let current_output = seat.active_output(); - let active = self.common.shell.active_space(¤t_output).handle; - let next_output = self - .common - .shell - .next_output(¤t_output, direction) - .cloned(); + let (active, next_output) = { + let shell = self.common.shell.read().unwrap(); + + ( + shell.active_space(¤t_output).handle, + shell.next_output(¤t_output, direction).cloned(), + ) + }; if let Some(next_output) = next_output { self.common - .shell .migrate_workspace(¤t_output, &next_output, &active); } } Action::Focus(focus) => { - let result = self.common.shell.next_focus(focus, seat); + let result = self.common.shell.read().unwrap().next_focus(focus, seat); match result { FocusResult::None => { @@ -2029,7 +2255,13 @@ impl State { } } Action::Move(direction) => { - match self.common.shell.move_current_element(direction, seat) { + let res = self + .common + .shell + .write() + .unwrap() + .move_current_element(direction, seat); + match res { MoveResult::MoveFurther(_move_further) => self.handle_action( Action::MoveToOutput(direction), seat, @@ -2044,10 +2276,11 @@ impl State { } _ => { let current_output = seat.active_output(); - let workspace = self.common.shell.active_space(¤t_output); + let mut shell = self.common.shell.write().unwrap(); + let workspace = shell.active_space(¤t_output); if let Some(focused_window) = workspace.focus_stack.get(seat).last() { if workspace.is_tiled(focused_window) { - self.common.shell.set_overview_mode( + shell.set_overview_mode( Some(Trigger::KeyboardMove(pattern.modifiers)), self.common.event_loop_handle.clone(), ); @@ -2058,7 +2291,9 @@ impl State { } Action::SwapWindow => { let current_output = seat.active_output(); - let workspace = self.common.shell.active_space_mut(¤t_output); + let mut shell = self.common.shell.write().unwrap(); + + let workspace = shell.active_space_mut(¤t_output); if workspace.get_fullscreen().is_some() { return; // TODO, is this what we want? Maybe disengage fullscreen instead? } @@ -2068,7 +2303,7 @@ impl State { if let Some(descriptor) = workspace.node_desc(focus) { let grab = SwapWindowGrab::new(seat.clone(), descriptor.clone()); keyboard_handle.set_grab(grab, serial); - self.common.shell.set_overview_mode( + shell.set_overview_mode( Some(Trigger::KeyboardSwap(pattern, descriptor)), self.common.event_loop_handle.clone(), ); @@ -2077,48 +2312,66 @@ impl State { } Action::Minimize => { let current_output = seat.active_output(); - let workspace = self.common.shell.active_space_mut(¤t_output); + let mut shell = self.common.shell.write().unwrap(); + let workspace = shell.active_space_mut(¤t_output); let focus_stack = workspace.focus_stack.get(seat); let focused_window = focus_stack.last().cloned(); if let Some(window) = focused_window { - self.common.shell.minimize_request(&window); + shell.minimize_request(&window); } } Action::Maximize => { let current_output = seat.active_output(); - let workspace = self.common.shell.active_space_mut(¤t_output); + let mut shell = self.common.shell.write().unwrap(); + let workspace = shell.active_space(¤t_output); let focus_stack = workspace.focus_stack.get(seat); let focused_window = focus_stack.last().cloned(); if let Some(window) = focused_window { - self.common.shell.maximize_toggle(&window, seat); + shell.maximize_toggle(&window, seat); } } - Action::Resizing(direction) => self.common.shell.set_resize_mode( + Action::Resizing(direction) => self.common.shell.write().unwrap().set_resize_mode( Some((pattern, direction)), &self.common.config, self.common.event_loop_handle.clone(), ), Action::_ResizingInternal(direction, edge, state) => { if state == KeyState::Pressed { - self.common.shell.resize(seat, direction, edge); + self.common + .shell + .write() + .unwrap() + .resize(seat, direction, edge); } else { - self.common.shell.finish_resize(direction, edge); + self.common + .shell + .write() + .unwrap() + .finish_resize(direction, edge); } } Action::ToggleOrientation => { let output = seat.active_output(); - let workspace = self.common.shell.active_space_mut(&output); + let mut shell = self.common.shell.write().unwrap(); + let workspace = shell.active_space_mut(&output); workspace.tiling_layer.update_orientation(None, &seat); } Action::Orientation(orientation) => { let output = seat.active_output(); - let workspace = self.common.shell.active_space_mut(&output); + let mut shell = self.common.shell.write().unwrap(); + let workspace = shell.active_space_mut(&output); workspace .tiling_layer .update_orientation(Some(orientation), &seat); } Action::ToggleStacking => { - if let Some(new_focus) = self.common.shell.toggle_stacking_focused(seat) { + let res = self + .common + .shell + .write() + .unwrap() + .toggle_stacking_focused(seat); + if let Some(new_focus) = res { Shell::set_focus(self, Some(&new_focus), seat, Some(serial)); } } @@ -2129,9 +2382,16 @@ impl State { ) { let autotile = !self.common.config.cosmic_conf.autotile; self.common.config.cosmic_conf.autotile = autotile; - self.common - .shell - .update_autotile(self.common.config.cosmic_conf.autotile); + + { + let mut shell = self.common.shell.write().unwrap(); + let shell_ref = &mut *shell; + shell_ref.workspaces.update_autotile( + self.common.config.cosmic_conf.autotile, + &mut self.common.workspace_state.update(), + shell_ref.seats.iter(), + ); + } let config = self.common.config.cosmic_helper.clone(); thread::spawn(move || { if let Err(err) = config.set("autotile", autotile) { @@ -2140,39 +2400,42 @@ impl State { }); } else { let output = seat.active_output(); - let workspace = self.common.shell.workspaces.active_mut(&output); - let mut guard = self.common.shell.workspace_state.update(); + let mut shell = self.common.shell.write().unwrap(); + let workspace = shell.workspaces.active_mut(&output); + let mut guard = self.common.workspace_state.update(); workspace.toggle_tiling(seat, &mut guard); } } Action::ToggleWindowFloating => { let output = seat.active_output(); - let workspace = self.common.shell.active_space_mut(&output); + let mut shell = self.common.shell.write().unwrap(); + let workspace = shell.active_space_mut(&output); workspace.toggle_floating_window_focused(seat); } Action::ToggleSticky => { - self.common.shell.toggle_sticky_current(seat); + self.common + .shell + .write() + .unwrap() + .toggle_sticky_current(seat); } Action::Spawn(command) => { - let (token, data) = self - .common - .shell - .xdg_activation_state - .create_external_token(None); + let mut shell = self.common.shell.write().unwrap(); + + let (token, data) = self.common.xdg_activation_state.create_external_token(None); let (token, data) = (token.clone(), data.clone()); - let seat = self.common.shell.seats.last_active(); - let output = seat.active_output(); - let workspace = self.common.shell.active_space_mut(&output); + let output = shell.seats.last_active().active_output(); + let workspace = shell.active_space_mut(&output); workspace.pending_tokens.insert(token.clone()); let handle = workspace.handle; + std::mem::drop(shell); data.user_data .insert_if_missing(move || ActivationContext::Workspace(handle)); let wayland_display = self.common.socket.clone(); let display = self .common - .shell .xwayland_state .as_ref() .map(|s| format!(":{}", s.display)) @@ -2201,6 +2464,7 @@ impl State { } } + // TODO: Try to get rid of the *mutable* Shell references (needed for hovered_stack in floating_layout) pub fn surface_under( global_pos: Point, output: &Output, @@ -2222,7 +2486,7 @@ impl State { }); } - if let Some(window) = shell.workspaces.active_mut(output).get_fullscreen() { + if let Some(window) = shell.workspaces.active(output).1.get_fullscreen() { let layers = layer_map_for_output(output); if let Some(layer) = layers.layer_under(WlrLayer::Overlay, relative_pos.as_logical()) { let layer_loc = layers.layer_geometry(layer).unwrap().loc; @@ -2332,63 +2596,63 @@ impl State { None } } +} - pub fn to_next_workspace( - &mut self, - seat: &Seat, - gesture: bool, - ) -> Result>, InvalidWorkspaceIndex> { - let current_output = seat.active_output(); - let workspace = self - .common - .shell - .workspaces - .active_num(¤t_output) - .1 - .saturating_add(1); - - self.common.shell.activate( - ¤t_output, - workspace, - if gesture { - WorkspaceDelta::new_gesture() - } else { - WorkspaceDelta::new_shortcut() - }, - ) - } +fn to_next_workspace( + shell: &mut Shell, + seat: &Seat, + gesture: bool, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, +) -> Result>, InvalidWorkspaceIndex> { + let current_output = seat.active_output(); + let workspace = shell + .workspaces + .active_num(¤t_output) + .1 + .saturating_add(1); + + shell.activate( + ¤t_output, + workspace, + if gesture { + WorkspaceDelta::new_gesture() + } else { + WorkspaceDelta::new_shortcut() + }, + workspace_state, + ) +} - pub fn to_previous_workspace( - &mut self, - seat: &Seat, - gesture: bool, - ) -> Result>, InvalidWorkspaceIndex> { - let current_output = seat.active_output(); - let workspace = self - .common - .shell - .workspaces - .active_num(¤t_output) - .1 - .saturating_sub(1); - - self.common.shell.activate( - ¤t_output, - workspace, - if gesture { - WorkspaceDelta::new_gesture() - } else { - WorkspaceDelta::new_shortcut() - }, - ) - } +fn to_previous_workspace( + shell: &mut Shell, + seat: &Seat, + gesture: bool, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, +) -> Result>, InvalidWorkspaceIndex> { + let current_output = seat.active_output(); + let workspace = shell + .workspaces + .active_num(¤t_output) + .1 + .saturating_sub(1); + + shell.activate( + ¤t_output, + workspace, + if gesture { + WorkspaceDelta::new_gesture() + } else { + WorkspaceDelta::new_shortcut() + }, + workspace_state, + ) } fn cursor_sessions_for_output( - state: &Common, + shell: &Shell, output: &Output, ) -> impl Iterator { - let workspace = state.shell.active_space(&output); + let workspace = shell.active_space(&output); let maybe_fullscreen = workspace.get_fullscreen(); workspace .cursor_sessions() @@ -2405,18 +2669,18 @@ fn cursor_sessions_for_output( // TODO Is it possible to determine mapping for external touchscreen? // Support map_to_region like sway? fn mapped_output_for_device<'a, D: Device + 'static>( - state: &'a Common, + config: &Config, + shell: &'a Shell, device: &D, ) -> Option<&'a Output> { let map_to_output = if let Some(device) = ::downcast_ref::(device) { - state - .config + config .map_to_output(device) - .and_then(|name| state.shell.outputs().find(|output| output.name() == name)) + .and_then(|name| shell.outputs().find(|output| output.name() == name)) } else { None }; - map_to_output.or_else(|| state.shell.builtin_output()) + map_to_output.or_else(|| shell.builtin_output()) } // FIXME: When f64::next_down reaches stable rust, use that instead diff --git a/src/main.rs b/src/main.rs index 2d573109..28d51a93 100644 --- a/src/main.rs +++ b/src/main.rs @@ -92,7 +92,16 @@ fn main() -> Result<()> { // run the event loop event_loop.run(None, &mut state, |state| { // shall we shut down? - if state.common.shell.outputs().next().is_none() || state.common.should_stop { + if state + .common + .shell + .read() + .unwrap() + .outputs() + .next() + .is_none() + || state.common.should_stop + { info!("Shutting down"); state.common.event_loop_signal.stop(); state.common.event_loop_signal.wakeup(); @@ -100,29 +109,25 @@ fn main() -> Result<()> { } // trigger routines - let clients = state.common.shell.update_animations(); + let clients = state.common.shell.write().unwrap().update_animations(); { let dh = state.common.display_handle.clone(); for client in clients.values() { client_compositor_state(&client).blocker_cleared(state, &dh); } } - state.common.shell.refresh(); + state.common.refresh(); state::Common::refresh_focus(state); state.common.update_x11_stacking_order(); - if state.common.shell.animations_going() { - for output in state - .common - .shell - .outputs() - .cloned() - .collect::>() - .into_iter() - { - state - .backend - .schedule_render(&state.common.event_loop_handle, &output); + { + let shell = state.common.shell.read().unwrap(); + if shell.animations_going() { + for output in shell.outputs().cloned().collect::>().into_iter() { + state + .backend + .schedule_render(&state.common.event_loop_handle, &output); + } } } @@ -181,7 +186,14 @@ fn init_wayland_display( let node = match &state.backend { BackendData::Kms(kms_state) if kms_state.auto_assign => kms_state .target_node_for_output( - &state.common.shell.seats.last_active().active_output(), + &state + .common + .shell + .read() + .unwrap() + .seats + .last_active() + .active_output(), ), _ => None, }; diff --git a/src/session.rs b/src/session.rs index ddcff172..72eff23f 100644 --- a/src/session.rs +++ b/src/session.rs @@ -70,13 +70,7 @@ pub fn get_env(state: &State) -> Result> { .into_string() .map_err(|_| anyhow!("wayland socket is no valid utf-8 string?"))?, ); - if let Some(display) = state - .common - .shell - .xwayland_state - .as_ref() - .map(|s| s.display) - { + if let Some(display) = state.common.xwayland_state.as_ref().map(|s| s.display) { env.insert(String::from("DISPLAY"), format!(":{}", display)); } Ok(env) diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index b9e1b04c..bdcaef3b 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -5,7 +5,7 @@ use crate::{ focus::{target::PointerFocusTarget, FocusDirection}, grabs::{ReleaseMode, ResizeEdge}, layout::tiling::NodeDesc, - Direction, Shell, + Direction, }, state::State, utils::{ @@ -664,14 +664,25 @@ impl Program for CosmicStackInternal { let active = self.active.load(Ordering::SeqCst); if let Some(surface) = self.windows.lock().unwrap()[active].wl_surface() { loop_handle.insert_idle(move |state| { - Shell::move_request( - state, + let res = state.common.shell.write().unwrap().move_request( &surface, &seat, serial, ReleaseMode::NoMouseButtons, false, + &state.common.config, + &state.common.event_loop_handle, + &state.common.xdg_activation_state, ); + if let Some((grab, focus)) = res { + if grab.is_touch_grab() { + seat.get_touch().unwrap().set_grab(state, grab, serial); + } else { + seat.get_pointer() + .unwrap() + .set_grab(state, grab, serial, focus); + } + } }); } } @@ -683,7 +694,13 @@ impl Program for CosmicStackInternal { *self.potential_drag.lock().unwrap() = None; if let Some(surface) = self.windows.lock().unwrap().get(idx).cloned() { loop_handle.insert_idle(move |state| { - if let Some(mapped) = state.common.shell.element_for_surface(&surface) { + if let Some(mapped) = state + .common + .shell + .read() + .unwrap() + .element_for_surface(&surface) + { mapped.stack_ref().unwrap().set_active(&surface); } }); @@ -703,11 +720,10 @@ impl Program for CosmicStackInternal { let active = self.active.load(Ordering::SeqCst); if let Some(surface) = self.windows.lock().unwrap()[active].wl_surface() { loop_handle.insert_idle(move |state| { - if let Some(mapped) = - state.common.shell.element_for_surface(&surface).cloned() - { + let shell = state.common.shell.read().unwrap(); + if let Some(mapped) = shell.element_for_surface(&surface).cloned() { let position = if let Some((output, set)) = - state.common.shell.workspaces.sets.iter().find(|(_, set)| { + shell.workspaces.sets.iter().find(|(_, set)| { set.sticky_layer.mapped().any(|m| m == &mapped) }) { set.sticky_layer @@ -715,9 +731,7 @@ impl Program for CosmicStackInternal { .unwrap() .loc .to_global(output) - } else if let Some(workspace) = - state.common.shell.space_for_mut(&mapped) - { + } else if let Some(workspace) = shell.space_for(&mapped) { let Some(elem_geo) = workspace.element_geometry(&mapped) else { return; }; @@ -732,14 +746,22 @@ impl Program for CosmicStackInternal { .current_location() .to_i32_round(); cursor.y -= TAB_HEIGHT; - Shell::menu_request( - state, + let res = shell.menu_request( &surface, &seat, serial, cursor - position.as_logical(), true, + &state.common.config, + &state.common.event_loop_handle, ); + + std::mem::drop(shell); + if let Some((grab, focus)) = res { + seat.get_pointer() + .unwrap() + .set_grab(state, grab, serial, focus); + } } }); } @@ -749,28 +771,36 @@ impl Program for CosmicStackInternal { if let Some((seat, serial)) = self.last_seat.lock().unwrap().clone() { if let Some(surface) = self.windows.lock().unwrap()[idx].wl_surface() { loop_handle.insert_idle(move |state| { - if let Some(mapped) = - state.common.shell.element_for_surface(&surface).cloned() - { - if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { + let shell = state.common.shell.read().unwrap(); + if let Some(mapped) = shell.element_for_surface(&surface).cloned() { + if let Some(workspace) = shell.space_for(&mapped) { let Some(elem_geo) = workspace.element_geometry(&mapped) else { return; }; let position = elem_geo.loc.to_global(&workspace.output); + let mut cursor = seat .get_pointer() .unwrap() .current_location() .to_i32_round(); cursor.y -= TAB_HEIGHT; - Shell::menu_request( - state, + let res = shell.menu_request( &surface, &seat, serial, cursor - position.as_logical(), false, + &state.common.config, + &state.common.event_loop_handle, ); + + std::mem::drop(shell); + if let Some((grab, focus)) = res { + seat.get_pointer() + .unwrap() + .set_grab(state, grab, serial, focus); + } } } }); @@ -1175,18 +1205,30 @@ impl PointerTarget for CosmicStack { .with_program(|p| p.windows.lock().unwrap().get(dragged_out).cloned()) { let seat = seat.clone(); + let serial = event.serial; surface.try_force_undecorated(false); surface.send_configure(); if let Some(surface) = surface.wl_surface() { let _ = data.common.event_loop_handle.insert_idle(move |state| { - Shell::move_request( - state, + let res = state.common.shell.write().unwrap().move_request( &surface, &seat, - None, + serial, ReleaseMode::NoMouseButtons, true, - ) + &state.common.config, + &state.common.event_loop_handle, + &state.common.xdg_activation_state, + ); + if let Some((grab, focus)) = res { + if grab.is_touch_grab() { + seat.get_touch().unwrap().set_grab(state, grab, serial); + } else { + seat.get_pointer() + .unwrap() + .set_grab(state, grab, serial, focus); + } + } }); } } @@ -1220,8 +1262,7 @@ impl PointerTarget for CosmicStack { return; }; self.0.loop_handle().insert_idle(move |state| { - Shell::resize_request( - state, + let res = state.common.shell.write().unwrap().resize_request( &surface, &seat, serial, @@ -1236,7 +1277,16 @@ impl PointerTarget for CosmicStack { Focus::ResizeRight => ResizeEdge::RIGHT, Focus::Header => unreachable!(), }, - ) + ); + if let Some((grab, focus)) = res { + if grab.is_touch_grab() { + seat.get_touch().unwrap().set_grab(state, grab, serial); + } else { + seat.get_pointer() + .unwrap() + .set_grab(state, grab, serial, focus); + } + } }); } None => {} @@ -1279,14 +1329,25 @@ impl PointerTarget for CosmicStack { surface.send_configure(); if let Some(surface) = surface.wl_surface() { let _ = data.common.event_loop_handle.insert_idle(move |state| { - Shell::move_request( - state, + let res = state.common.shell.write().unwrap().move_request( &surface, &seat, - None, + serial, ReleaseMode::NoMouseButtons, true, - ) + &state.common.config, + &state.common.event_loop_handle, + &state.common.xdg_activation_state, + ); + if let Some((grab, focus)) = res { + if grab.is_touch_grab() { + seat.get_touch().unwrap().set_grab(state, grab, serial); + } else { + seat.get_pointer() + .unwrap() + .set_grab(state, grab, serial, focus); + } + } }); } } diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index e80105d1..fb28150a 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -3,7 +3,6 @@ use crate::{ shell::{ focus::target::PointerFocusTarget, grabs::{ReleaseMode, ResizeEdge}, - Shell, }, state::State, utils::{ @@ -387,14 +386,25 @@ impl Program for CosmicWindowInternal { if let Some((seat, serial)) = self.last_seat.lock().unwrap().clone() { if let Some(surface) = self.window.wl_surface() { loop_handle.insert_idle(move |state| { - Shell::move_request( - state, + let res = state.common.shell.write().unwrap().move_request( &surface, &seat, serial, ReleaseMode::NoMouseButtons, false, + &state.common.config, + &state.common.event_loop_handle, + &state.common.xdg_activation_state, ); + if let Some((grab, focus)) = res { + if grab.is_touch_grab() { + seat.get_touch().unwrap().set_grab(state, grab, serial); + } else { + seat.get_pointer() + .unwrap() + .set_grab(state, grab, serial, focus); + } + } }); } } @@ -402,10 +412,9 @@ impl Program for CosmicWindowInternal { Message::Minimize => { if let Some(surface) = self.window.wl_surface() { loop_handle.insert_idle(move |state| { - if let Some(mapped) = - state.common.shell.element_for_surface(&surface).cloned() - { - state.common.shell.minimize_request(&mapped) + let mut shell = state.common.shell.write().unwrap(); + if let Some(mapped) = shell.element_for_surface(&surface).cloned() { + shell.minimize_request(&mapped) } }); } @@ -413,11 +422,10 @@ impl Program for CosmicWindowInternal { Message::Maximize => { if let Some(surface) = self.window.wl_surface() { loop_handle.insert_idle(move |state| { - if let Some(mapped) = - state.common.shell.element_for_surface(&surface).cloned() - { - let seat = state.common.shell.seats.last_active().clone(); - state.common.shell.maximize_toggle(&mapped, &seat) + let mut shell = state.common.shell.write().unwrap(); + if let Some(mapped) = shell.element_for_surface(&surface).cloned() { + let seat = shell.seats.last_active().clone(); + shell.maximize_toggle(&mapped, &seat) } }); } @@ -427,11 +435,10 @@ impl Program for CosmicWindowInternal { if let Some((seat, serial)) = self.last_seat.lock().unwrap().clone() { if let Some(surface) = self.window.wl_surface() { loop_handle.insert_idle(move |state| { - if let Some(mapped) = - state.common.shell.element_for_surface(&surface).cloned() - { + let shell = state.common.shell.read().unwrap(); + if let Some(mapped) = shell.element_for_surface(&surface).cloned() { let position = if let Some((output, set)) = - state.common.shell.workspaces.sets.iter().find(|(_, set)| { + shell.workspaces.sets.iter().find(|(_, set)| { set.sticky_layer.mapped().any(|m| m == &mapped) }) { set.sticky_layer @@ -439,9 +446,7 @@ impl Program for CosmicWindowInternal { .unwrap() .loc .to_global(output) - } else if let Some(workspace) = - state.common.shell.space_for_mut(&mapped) - { + } else if let Some(workspace) = shell.space_for(&mapped) { let Some(elem_geo) = workspace.element_geometry(&mapped) else { return; }; @@ -450,20 +455,24 @@ impl Program for CosmicWindowInternal { return; }; - let mut cursor = seat - .get_pointer() - .unwrap() - .current_location() - .to_i32_round(); + let pointer = seat.get_pointer().unwrap(); + let mut cursor = pointer.current_location().to_i32_round(); cursor.y -= SSD_HEIGHT; - Shell::menu_request( - state, + + let res = shell.menu_request( &surface, &seat, serial, cursor - position.as_logical(), false, + &state.common.config, + &state.common.event_loop_handle, ); + + std::mem::drop(shell); + if let Some((grab, focus)) = res { + pointer.set_grab(state, grab, serial, focus); + } } }); } @@ -734,8 +743,7 @@ impl PointerTarget for CosmicWindow { return; }; self.0.loop_handle().insert_idle(move |state| { - Shell::resize_request( - state, + let res = state.common.shell.write().unwrap().resize_request( &surface, &seat, serial, @@ -750,7 +758,17 @@ impl PointerTarget for CosmicWindow { Focus::ResizeRight => ResizeEdge::RIGHT, Focus::Header => unreachable!(), }, - ) + ); + + if let Some((grab, focus)) = res { + if grab.is_touch_grab() { + seat.get_touch().unwrap().set_grab(state, grab, serial); + } else { + seat.get_pointer() + .unwrap() + .set_grab(state, grab, serial, focus); + } + } }); } None => {} diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index 69749acb..2de9c8cb 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -107,9 +107,13 @@ impl Shell { ) { let element = match target { Some(KeyboardFocusTarget::Element(mapped)) => Some(mapped.clone()), - Some(KeyboardFocusTarget::Fullscreen(window)) => { - state.common.shell.element_for_surface(window).cloned() - } + Some(KeyboardFocusTarget::Fullscreen(window)) => state + .common + .shell + .read() + .unwrap() + .element_for_surface(window) + .cloned(), _ => None, }; @@ -117,7 +121,12 @@ impl Shell { if mapped.is_minimized() { return; } - state.common.shell.append_focus_stack(&mapped, seat); + state + .common + .shell + .write() + .unwrap() + .append_focus_stack(&mapped, seat); } // update keyboard focus @@ -130,7 +139,7 @@ impl Shell { ); } - state.common.shell.update_active(); + state.common.shell.write().unwrap().update_active(); } pub fn append_focus_stack(&mut self, mapped: &CosmicMapped, seat: &Seat) { @@ -233,25 +242,27 @@ fn raise_with_children(floating_layer: &mut FloatingLayout, focused: &CosmicMapp impl Common { pub fn refresh_focus(state: &mut State) { - for seat in state + let seats = state .common .shell + .read() + .unwrap() .seats .iter() .cloned() - .collect::>() - .into_iter() - { + .collect::>(); + for seat in seats.into_iter() { + let mut shell = state.common.shell.write().unwrap(); let output = seat.active_output(); - if !state.common.shell.outputs().any(|o| o == &output) { - seat.set_active_output(&state.common.shell.outputs().next().unwrap()); + if !shell.outputs().any(|o| o == &output) { + seat.set_active_output(&shell.outputs().next().unwrap()); continue; } let last_known_focus = ActiveFocus::get(&seat); if let Some(target) = last_known_focus { if target.alive() { - if focus_target_is_valid(&mut state.common.shell, &seat, &output, target) { + if focus_target_is_valid(&mut *shell, &seat, &output, target) { continue; // Focus is valid } else { trace!("Wrong Window, focus fixup"); @@ -266,6 +277,8 @@ impl Common { if !popup_grab.has_ended() { if let Some(new) = popup_grab.current_grab() { trace!("restore focus to previous popup grab"); + std::mem::drop(shell); + if let Some(keyboard) = seat.get_keyboard() { keyboard.set_focus( state, @@ -285,7 +298,7 @@ impl Common { trace!("Surface dead, focus fixup"); } } else { - let workspace = state.common.shell.active_space(&output); + let workspace = shell.active_space(&output); let focus_stack = workspace.focus_stack.get(&seat); if focus_stack.last().is_none() { @@ -309,7 +322,9 @@ impl Common { } // update keyboard focus - let target = update_focus_target(&state.common.shell, &seat, &output); + let target = update_focus_target(&*shell, &seat, &output); + std::mem::drop(shell); + if let Some(keyboard) = seat.get_keyboard() { debug!("Restoring focus to {:?}", target.as_ref()); keyboard.set_focus(state, target.clone(), SERIAL_COUNTER.next_serial()); @@ -318,7 +333,7 @@ impl Common { } } - state.common.shell.update_active() + state.common.shell.write().unwrap().update_active() } } diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index 6fc38ced..03bcc3f7 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -132,7 +132,7 @@ impl PointerFocusTarget { } } - pub fn toplevel(&self, data: &mut State) -> Option { + pub fn toplevel(&self, shell: &Shell) -> Option { match &self { PointerFocusTarget::WlSurface { toplevel: Some(PointerFocusToplevel::Surface(surface)), @@ -142,12 +142,7 @@ impl PointerFocusTarget { toplevel: Some(PointerFocusToplevel::Popup(PopupKind::Xdg(popup))), .. } => get_popup_toplevel(popup) - .and_then(|s| { - data.common - .shell - .element_for_surface(&s) - .map(|mapped| (mapped, s)) - }) + .and_then(|s| shell.element_for_surface(&s).map(|mapped| (mapped, s))) .and_then(|(m, s)| { m.windows() .find(|(w, _)| w.wl_surface().map(|s2| s == s2).unwrap_or(false)) @@ -210,7 +205,8 @@ impl IsAlive for KeyboardFocusTarget { impl PointerTarget for PointerFocusTarget { fn enter(&self, seat: &Seat, data: &mut State, event: &PointerMotionEvent) { - if let Some(element) = self.toplevel(data) { + let toplevel = self.toplevel(&*data.common.shell.read().unwrap()); + if let Some(element) = toplevel { for session in element.cursor_sessions() { session.set_cursor_pos(Some( event @@ -238,7 +234,8 @@ impl PointerTarget for PointerFocusTarget { } } fn motion(&self, seat: &Seat, data: &mut State, event: &PointerMotionEvent) { - if let Some(element) = self.toplevel(data) { + let toplevel = self.toplevel(&*data.common.shell.read().unwrap()); + if let Some(element) = toplevel { for session in element.cursor_sessions() { session.set_cursor_pos(Some( event @@ -308,7 +305,8 @@ impl PointerTarget for PointerFocusTarget { } } fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) { - if let Some(element) = self.toplevel(data) { + let toplevel = self.toplevel(&*data.common.shell.read().unwrap()); + if let Some(element) = toplevel { for session in element.cursor_sessions() { session.set_cursor_pos(None); session.set_cursor_hotspot((0, 0)); diff --git a/src/shell/grabs/menu/default.rs b/src/shell/grabs/menu/default.rs index 9f49e7f5..09b6a1cf 100644 --- a/src/shell/grabs/menu/default.rs +++ b/src/shell/grabs/menu/default.rs @@ -1,4 +1,4 @@ -use smithay::wayland::seat::WaylandFocus; +use smithay::{input::pointer::MotionEvent, utils::SERIAL_COUNTER, wayland::seat::WaylandFocus}; use crate::{ config::{Action, StaticConfig}, @@ -6,7 +6,7 @@ use crate::{ shell::{ element::{CosmicMapped, CosmicWindow}, grabs::ReleaseMode, - CosmicSurface, Shell, + CosmicSurface, PointGlobalExt, Shell, }, state::State, utils::{prelude::SeatExt, screenshot::screenshot_window}, @@ -15,61 +15,57 @@ use crate::{ use super::{Item, ResizeEdge}; fn toggle_stacking(state: &mut State, mapped: &CosmicMapped) { - let seat = state.common.shell.seats.last_active().clone(); - if let Some(new_focus) = state.common.shell.toggle_stacking(mapped) { + let mut shell = state.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); + if let Some(new_focus) = shell.toggle_stacking(mapped) { + std::mem::drop(shell); Shell::set_focus(state, Some(&new_focus), &seat, None); } } fn move_prev_workspace(state: &mut State, mapped: &CosmicMapped) { - let seat = state.common.shell.seats.last_active().clone(); + let mut shell = state.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); let (current_handle, output) = { - let Some(ws) = state.common.shell.space_for(mapped) else { + let Some(ws) = shell.space_for(mapped) else { return; }; (ws.handle, ws.output.clone()) }; - let maybe_handle = state - .common - .shell + let maybe_handle = shell .workspaces .spaces_for_output(&output) .enumerate() .find_map(|(i, space)| (space.handle == current_handle).then_some(i)) .and_then(|i| i.checked_sub(1)) - .and_then(|i| { - state - .common - .shell - .workspaces - .get(i, &output) - .map(|s| s.handle) - }); + .and_then(|i| shell.workspaces.get(i, &output).map(|s| s.handle)); if let Some(prev_handle) = maybe_handle { - if let Some((target, _)) = state.common.shell.move_window( + let res = shell.move_window( Some(&seat), mapped, ¤t_handle, &prev_handle, true, None, - ) { + &mut state.common.workspace_state.update(), + ); + if let Some((target, _)) = res { + std::mem::drop(shell); Shell::set_focus(state, Some(&target), &seat, None); } } } fn move_next_workspace(state: &mut State, mapped: &CosmicMapped) { - let seat = state.common.shell.seats.last_active().clone(); + let mut shell = state.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); let (current_handle, output) = { - let Some(ws) = state.common.shell.space_for(mapped) else { + let Some(ws) = shell.space_for(mapped) else { return; }; (ws.handle, ws.output.clone()) }; - let maybe_handle = state - .common - .shell + let maybe_handle = shell .workspaces .spaces_for_output(&output) .skip_while(|space| space.handle != current_handle) @@ -77,14 +73,17 @@ fn move_next_workspace(state: &mut State, mapped: &CosmicMapped) { .next() .map(|space| space.handle); if let Some(next_handle) = maybe_handle { - if let Some((target, _point)) = state.common.shell.move_window( + let res = shell.move_window( Some(&seat), mapped, ¤t_handle, &next_handle, true, None, - ) { + &mut state.common.workspace_state.update(), + ); + if let Some((target, _point)) = res { + std::mem::drop(shell); Shell::set_focus(state, Some(&target), &seat, None) } } @@ -114,9 +113,10 @@ pub fn tab_items( ) .into(); - let seat = state.common.shell.seats.last_active(); + let mut shell = state.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); let output = seat.active_output(); - let workspace = state.common.shell.workspaces.active_mut(&output); + let workspace = shell.workspaces.active_mut(&output); if is_tiled { for mapped in workspace .mapped() @@ -127,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); @@ -197,7 +197,12 @@ pub fn window_items( Item::new(fl!("window-menu-minimize"), move |handle| { let mapped = minimize_clone.clone(); let _ = handle.insert_idle(move |state| { - state.common.shell.minimize_request(&mapped); + state + .common + .shell + .write() + .unwrap() + .minimize_request(&mapped); }); }) .shortcut(config.get_shortcut_for_action(&Action::Minimize)), @@ -206,8 +211,9 @@ 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.shell.seats.last_active().clone(); - state.common.shell.maximize_toggle(&mapped, &seat); + let mut shell = state.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); + shell.maximize_toggle(&mapped, &seat); }); }) .shortcut(config.get_shortcut_for_action(&Action::Maximize)) @@ -217,8 +223,9 @@ 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.shell.seats.last_active().clone(); - if let Some(ws) = state.common.shell.space_for_mut(&tile_clone) { + let mut shell = state.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); + if let Some(ws) = shell.space_for_mut(&tile_clone) { ws.toggle_floating_window(&seat, &tile_clone); } }); @@ -238,8 +245,36 @@ 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.shell.seats.last_active().clone(); - Shell::move_request(state, &surface, &seat, None, ReleaseMode::Click, false); + let mut shell = state.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); + let res = shell.move_request( + &surface, + &seat, + None, + ReleaseMode::Click, + false, + &state.common.config, + &state.common.event_loop_handle, + &state.common.xdg_activation_state, + ); + + std::mem::drop(shell); + if let Some((grab, focus)) = res { + if grab.is_touch_grab() { + seat.get_touch().unwrap().set_grab( + state, + grab, + SERIAL_COUNTER.next_serial(), + ) + } else { + seat.get_pointer().unwrap().set_grab( + state, + grab, + SERIAL_COUNTER.next_serial(), + focus, + ); + } + } } }); })), @@ -249,32 +284,122 @@ 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.shell.seats.last_active().clone(); - Shell::menu_resize_request(state, &resize_clone, &seat, ResizeEdge::TOP); + let mut shell = state.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); + let res = shell.menu_resize_request(&resize_clone, &seat, ResizeEdge::TOP); + + std::mem::drop(shell); + if let Some(((target, loc), (grab, focus))) = res { + let serial = SERIAL_COUNTER.next_serial(); + if grab.is_touch_grab() { + seat.get_touch().unwrap().set_grab(state, grab, serial); + } else { + let pointer = seat.get_pointer().unwrap(); + pointer.motion( + state, + target, + &MotionEvent { + location: loc.as_logical().to_f64(), + serial, + time: 0, + }, + ); + pointer.frame(state); + pointer.set_grab(state, grab, serial, focus); + } + } }); }) .disabled(!possible_resizes.contains(ResizeEdge::TOP)), 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.shell.seats.last_active().clone(); - Shell::menu_resize_request(state, &resize_clone, &seat, ResizeEdge::LEFT); + let mut shell = state.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); + let res = shell.menu_resize_request(&resize_clone, &seat, ResizeEdge::LEFT); + + std::mem::drop(shell); + if let Some(((target, loc), (grab, focus))) = res { + let serial = SERIAL_COUNTER.next_serial(); + if grab.is_touch_grab() { + seat.get_touch().unwrap().set_grab(state, grab, serial); + } else { + let pointer = seat.get_pointer().unwrap(); + pointer.motion( + state, + target, + &MotionEvent { + location: loc.as_logical().to_f64(), + serial, + time: 0, + }, + ); + pointer.frame(state); + pointer.set_grab(state, grab, serial, focus); + } + } }); }) .disabled(!possible_resizes.contains(ResizeEdge::LEFT)), 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.shell.seats.last_active().clone(); - Shell::menu_resize_request(state, &resize_clone, &seat, ResizeEdge::RIGHT); + let mut shell = state.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); + let res = + shell.menu_resize_request(&resize_clone, &seat, ResizeEdge::RIGHT); + + std::mem::drop(shell); + if let Some(((target, loc), (grab, focus))) = res { + let serial = SERIAL_COUNTER.next_serial(); + if grab.is_touch_grab() { + seat.get_touch().unwrap().set_grab(state, grab, serial); + } else { + let pointer = seat.get_pointer().unwrap(); + pointer.motion( + state, + target, + &MotionEvent { + location: loc.as_logical().to_f64(), + serial, + time: 0, + }, + ); + pointer.frame(state); + pointer.set_grab(state, grab, serial, focus); + } + } }); }) .disabled(!possible_resizes.contains(ResizeEdge::RIGHT)), 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.shell.seats.last_active().clone(); - Shell::menu_resize_request(state, &resize_clone, &seat, ResizeEdge::BOTTOM); + let mut shell = state.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); + let res = + shell.menu_resize_request(&resize_clone, &seat, ResizeEdge::BOTTOM); + + std::mem::drop(shell); + if let Some(((target, loc), (grab, focus))) = res { + let serial = SERIAL_COUNTER.next_serial(); + if grab.is_touch_grab() { + seat.get_touch().unwrap().set_grab(state, grab, serial); + } else { + let pointer = seat.get_pointer().unwrap(); + pointer.motion( + state, + target, + &MotionEvent { + location: loc.as_logical().to_f64(), + serial, + time: 0, + }, + ); + pointer.frame(state); + pointer.set_grab(state, grab, serial, focus); + } + } }); }) .disabled(!possible_resizes.contains(ResizeEdge::BOTTOM)), @@ -301,8 +426,9 @@ 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.shell.seats.last_active().clone(); - state.common.shell.toggle_sticky(&seat, &mapped); + let mut shell = state.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); + 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 5b887336..09edfcd6 100644 --- a/src/shell/grabs/menu/mod.rs +++ b/src/shell/grabs/menu/mod.rs @@ -216,7 +216,14 @@ 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.shell.seats.last_active(); + let seat = state + .common + .shell + .read() + .unwrap() + .seats + .last_active() + .clone(); let grab_state = seat .user_data() .get::() @@ -301,7 +308,14 @@ 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.shell.seats.last_active(); + let seat = state + .common + .shell + .read() + .unwrap() + .seats + .last_active() + .clone(); let grab_state = seat .user_data() .get::() diff --git a/src/shell/grabs/mod.rs b/src/shell/grabs/mod.rs index 0181deb1..a6f508e6 100644 --- a/src/shell/grabs/mod.rs +++ b/src/shell/grabs/mod.rs @@ -153,6 +153,15 @@ impl From for ResizeGrab { } } +impl ResizeGrab { + pub fn is_touch_grab(&self) -> bool { + match self { + ResizeGrab::Floating(grab) => grab.is_touch_grab(), + ResizeGrab::Tiling(grab) => grab.is_touch_grab(), + } + } +} + impl PointerGrab for ResizeGrab { fn motion( &mut self, diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index 5c0e884c..5e87e715 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -16,6 +16,7 @@ use crate::{ CosmicMapped, CosmicSurface, Direction, ManagedLayer, }, utils::prelude::*, + wayland::protocols::toplevel_info::{toplevel_enter_output, toplevel_enter_workspace}, }; use calloop::LoopHandle; @@ -326,10 +327,10 @@ pub struct MoveGrab { impl MoveGrab { fn update_location(&mut self, state: &mut State, location: Point) { + let mut shell = state.common.shell.write().unwrap(); + let Some(current_output) = - state - .common - .shell + shell .outputs() .find(|output| { output.geometry().as_logical().overlaps_or_touches( @@ -341,9 +342,7 @@ impl MoveGrab { return; }; if self.cursor_output != current_output { - state - .common - .shell + shell .workspaces .active_mut(&self.cursor_output) .tiling_layer @@ -362,7 +361,7 @@ impl MoveGrab { let mut window_geo = self.window.geometry(); window_geo.loc += location.to_i32_round() + grab_state.window_offset; - for output in state.common.shell.outputs() { + for output in shell.outputs() { if let Some(overlap) = output.geometry().as_logical().intersection(window_geo) { if self.window_outputs.insert(output.clone()) { self.window.output_enter(output, overlap); @@ -380,10 +379,7 @@ impl MoveGrab { } } - let indicator_location = state - .common - .shell - .stacking_indicator(¤t_output, self.previous); + let indicator_location = shell.stacking_indicator(¤t_output, self.previous); if indicator_location.is_some() != grab_state.stacking_indicator.is_some() { grab_state.stacking_indicator = indicator_location.map(|geo| { let element = stack_hover( @@ -695,6 +691,13 @@ impl MoveGrab { pub fn is_tiling_grab(&self) -> bool { self.previous == ManagedLayer::Tiling } + + pub fn is_touch_grab(&self) -> bool { + match self.start_data { + GrabStartData::Touch(_) => true, + GrabStartData::Pointer(_) => false, + } + } } impl Drop for MoveGrab { @@ -716,23 +719,17 @@ impl Drop for MoveGrab { if grab_state.window.alive() { let window_location = (grab_state.location.to_i32_round() + grab_state.window_offset).as_global(); + let mut shell = state.common.shell.write().unwrap(); - let workspace_handle = state.common.shell.active_space(&output).handle; + let workspace_handle = shell.active_space(&output).handle; for old_output in window_outputs.iter().filter(|o| *o != &output) { grab_state.window.output_leave(old_output); } + for (window, _) in grab_state.window.windows() { - state - .common - .shell - .toplevel_info_state - .toplevel_enter_output(&window, &output); + toplevel_enter_output(&window, &output); if previous != ManagedLayer::Sticky { - state - .common - .shell - .toplevel_info_state - .toplevel_enter_workspace(&window, &workspace_handle); + toplevel_enter_workspace(&window, &workspace_handle); } } @@ -742,19 +739,15 @@ impl Drop for MoveGrab { window_location, grab_state.window.geometry().size.as_global(), )); - let set = state.common.shell.workspaces.sets.get_mut(&output).unwrap(); + let set = shell.workspaces.sets.get_mut(&output).unwrap(); let (window, location) = set .sticky_layer .drop_window(grab_state.window, window_location.to_local(&output)); Some((window, location.to_global(&output))) } - ManagedLayer::Tiling - if state.common.shell.active_space(&output).tiling_enabled => - { - let (window, location) = state - .common - .shell + ManagedLayer::Tiling if shell.active_space(&output).tiling_enabled => { + let (window, location) = shell .active_space_mut(&output) .tiling_layer .drop_window(grab_state.window); @@ -765,8 +758,8 @@ impl Drop for MoveGrab { window_location, grab_state.window.geometry().size.as_global(), )); - let theme = state.common.shell.theme.clone(); - let workspace = state.common.shell.active_space_mut(&output); + let theme = shell.theme.clone(); + let workspace = shell.active_space_mut(&output); let (window, location) = workspace.floating_layer.drop_window( grab_state.window, window_location.to_local(&workspace.output), @@ -775,7 +768,7 @@ impl Drop for MoveGrab { if previous == ManagedLayer::Floating { if let Some(sz) = grab_state.snapping_zone { if sz == SnappingZone::Maximize { - state.common.shell.maximize_toggle(&window, &seat); + shell.maximize_toggle(&window, &seat); } else { let directions = match sz { SnappingZone::Maximize => vec![], diff --git a/src/shell/layout/floating/grabs/resize.rs b/src/shell/layout/floating/grabs/resize.rs index 77c9cd79..cbfb8ee1 100644 --- a/src/shell/layout/floating/grabs/resize.rs +++ b/src/shell/layout/floating/grabs/resize.rs @@ -123,6 +123,13 @@ impl ResizeSurfaceGrab { false } + + pub fn is_touch_grab(&self) -> bool { + match self.start_data { + GrabStartData::Touch(_) => true, + GrabStartData::Pointer(_) => false, + } + } } impl PointerGrab for ResizeSurfaceGrab { diff --git a/src/shell/layout/tiling/grabs/resize.rs b/src/shell/layout/tiling/grabs/resize.rs index a9eaf1d4..8387576d 100644 --- a/src/shell/layout/tiling/grabs/resize.rs +++ b/src/shell/layout/tiling/grabs/resize.rs @@ -218,7 +218,8 @@ impl ResizeForkGrab { let delta = location - self.last_loc.as_logical(); if let Some(output) = self.output.upgrade() { - let tiling_layer = &mut data.common.shell.active_space_mut(&output).tiling_layer; + let mut shell = data.common.shell.write().unwrap(); + let tiling_layer = &mut shell.active_space_mut(&output).tiling_layer; let gaps = tiling_layer.gaps(); let tree = &mut tiling_layer.queue.trees.back_mut().unwrap().0; @@ -321,6 +322,13 @@ impl ResizeForkGrab { } false } + + pub fn is_touch_grab(&self) -> bool { + match self.start_data { + GrabStartData::Touch(_) => true, + GrabStartData::Pointer(_) => false, + } + } } impl PointerGrab for ResizeForkGrab { diff --git a/src/shell/layout/tiling/grabs/swap.rs b/src/shell/layout/tiling/grabs/swap.rs index b3f59407..bbba5353 100644 --- a/src/shell/layout/tiling/grabs/swap.rs +++ b/src/shell/layout/tiling/grabs/swap.rs @@ -39,7 +39,7 @@ impl KeyboardGrab for SwapWindowGrab { serial: Serial, time: u32, ) { - if !matches!(&data.common.shell.overview_mode, OverviewMode::Started(Trigger::KeyboardSwap(_, d), _) if d == &self.desc) + if !matches!(&data.common.shell.read().unwrap().overview_mode, OverviewMode::Started(Trigger::KeyboardSwap(_, d), _) if d == &self.desc) { handle.unset_grab(data, serial, false); return; diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 860cdd6a..5cc5970f 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -28,7 +28,13 @@ use crate::{ utils::{prelude::*, tween::EaseRectangle}, wayland::{ handlers::xdg_shell::popup::get_popup_toplevel, - protocols::{toplevel_info::ToplevelInfoState, workspace::WorkspaceHandle}, + protocols::{ + toplevel_info::{ + toplevel_enter_output, toplevel_enter_workspace, toplevel_leave_output, + toplevel_leave_workspace, + }, + workspace::WorkspaceHandle, + }, }, }; @@ -633,7 +639,6 @@ impl TilingLayout { seat: &Seat, focus_stack: impl Iterator + 'a, desc: NodeDesc, - toplevel_info_state: &mut ToplevelInfoState, ) -> Option { let this_handle = &desc.handle; let mut this_tree = this.queue.trees.back().unwrap().0.copy_clone(); @@ -662,13 +667,13 @@ impl TilingLayout { mapped.output_leave(&this.output); mapped.output_enter(&other.output, mapped.bbox()); for (ref surface, _) in mapped.windows() { - toplevel_info_state.toplevel_leave_output(surface, &this.output); - toplevel_info_state.toplevel_enter_output(surface, &other.output); + toplevel_leave_output(surface, &this.output); + toplevel_enter_output(surface, &other.output); } } for (ref surface, _) in mapped.windows() { - toplevel_info_state.toplevel_leave_workspace(surface, this_handle); - toplevel_info_state.toplevel_enter_workspace(surface, other_handle); + toplevel_leave_workspace(surface, this_handle); + toplevel_enter_workspace(surface, other_handle); } mapped.set_tiled(true); @@ -725,13 +730,13 @@ impl TilingLayout { mapped.output_leave(&this.output); mapped.output_enter(&other.output, mapped.bbox()); for (ref surface, _) in mapped.windows() { - toplevel_info_state.toplevel_leave_output(surface, &this.output); - toplevel_info_state.toplevel_enter_output(surface, &other.output); + toplevel_leave_output(surface, &this.output); + toplevel_enter_output(surface, &other.output); } } for (ref surface, _) in mapped.windows() { - toplevel_info_state.toplevel_leave_workspace(surface, this_handle); - toplevel_info_state.toplevel_enter_workspace(surface, other_handle); + toplevel_leave_workspace(surface, this_handle); + toplevel_enter_workspace(surface, other_handle); } *mapped.tiling_node_id.lock().unwrap() = Some(id.clone()); @@ -759,15 +764,13 @@ impl TilingLayout { mapped.output_leave(&this.output); mapped.output_enter(&other.output, mapped.bbox()); for (ref surface, _) in mapped.windows() { - toplevel_info_state - .toplevel_leave_output(surface, &this.output); - toplevel_info_state - .toplevel_enter_output(surface, &other.output); + toplevel_leave_output(surface, &this.output); + toplevel_enter_output(surface, &other.output); } } for (ref surface, _) in mapped.windows() { - toplevel_info_state.toplevel_leave_workspace(surface, this_handle); - toplevel_info_state.toplevel_enter_workspace(surface, other_handle); + toplevel_leave_workspace(surface, this_handle); + toplevel_enter_workspace(surface, other_handle); } *mapped.tiling_node_id.lock().unwrap() = Some(new_id.clone()); @@ -806,7 +809,6 @@ impl TilingLayout { mut other: Option<&mut Self>, this_desc: &NodeDesc, other_desc: &NodeDesc, - toplevel_info_state: &mut ToplevelInfoState, ) -> Option { let other_output = other .as_ref() @@ -860,13 +862,13 @@ impl TilingLayout { mapped.output_leave(&other_output); mapped.output_enter(&this.output, mapped.bbox()); for (ref surface, _) in mapped.windows() { - toplevel_info_state.toplevel_leave_output(surface, &other_output); - toplevel_info_state.toplevel_enter_output(surface, &this.output); + toplevel_leave_output(surface, &other_output); + toplevel_enter_output(surface, &this.output); } } for (ref surface, _) in mapped.windows() { - toplevel_info_state.toplevel_leave_workspace(surface, &other_desc.handle); - toplevel_info_state.toplevel_enter_workspace(surface, &this_desc.handle); + toplevel_leave_workspace(surface, &other_desc.handle); + toplevel_enter_workspace(surface, &this_desc.handle); } *mapped.tiling_node_id.lock().unwrap() = Some(this_desc.node.clone()); } @@ -875,13 +877,13 @@ impl TilingLayout { mapped.output_leave(&this.output); mapped.output_enter(&other_output, mapped.bbox()); for (ref surface, _) in mapped.windows() { - toplevel_info_state.toplevel_leave_output(surface, &this.output); - toplevel_info_state.toplevel_enter_output(surface, &other_output); + toplevel_leave_output(surface, &this.output); + toplevel_enter_output(surface, &other_output); } } for (ref surface, _) in mapped.windows() { - toplevel_info_state.toplevel_leave_workspace(surface, &this_desc.handle); - toplevel_info_state.toplevel_enter_workspace(surface, &other_desc.handle); + toplevel_leave_workspace(surface, &this_desc.handle); + toplevel_enter_workspace(surface, &other_desc.handle); } *mapped.tiling_node_id.lock().unwrap() = Some(other_desc.node.clone()); } @@ -914,17 +916,13 @@ impl TilingLayout { mapped.output_leave(&this.output); mapped.output_enter(&other_output, mapped.bbox()); for (ref surface, _) in mapped.windows() { - toplevel_info_state - .toplevel_leave_output(surface, &this.output); - toplevel_info_state - .toplevel_enter_output(surface, &other_output); + toplevel_leave_output(surface, &this.output); + toplevel_enter_output(surface, &other_output); } } for (ref surface, _) in mapped.windows() { - toplevel_info_state - .toplevel_leave_workspace(surface, &this_desc.handle); - toplevel_info_state - .toplevel_enter_workspace(surface, &other_desc.handle); + toplevel_leave_workspace(surface, &this_desc.handle); + toplevel_enter_workspace(surface, &other_desc.handle); } *mapped.tiling_node_id.lock().unwrap() = Some(new_id.clone()); } @@ -964,17 +962,13 @@ impl TilingLayout { mapped.output_leave(&other_output); mapped.output_enter(&this.output, mapped.bbox()); for (ref surface, _) in mapped.windows() { - toplevel_info_state - .toplevel_leave_output(surface, &other_output); - toplevel_info_state - .toplevel_enter_output(surface, &this.output); + toplevel_leave_output(surface, &other_output); + toplevel_enter_output(surface, &this.output); } } for (ref surface, _) in mapped.windows() { - toplevel_info_state - .toplevel_leave_workspace(surface, &other_desc.handle); - toplevel_info_state - .toplevel_enter_workspace(surface, &this_desc.handle); + toplevel_leave_workspace(surface, &other_desc.handle); + toplevel_enter_workspace(surface, &this_desc.handle); } *mapped.tiling_node_id.lock().unwrap() = Some(new_id.clone()); } @@ -1034,23 +1028,23 @@ impl TilingLayout { if this.output != other_output { surface.output_leave(&other_output); surface.output_enter(&this.output, surface.bbox()); - toplevel_info_state.toplevel_leave_output(&surface, &other_output); - toplevel_info_state.toplevel_enter_output(&surface, &this.output); + toplevel_leave_output(&surface, &other_output); + toplevel_enter_output(&surface, &this.output); } if this_desc.handle != other_desc.handle { - toplevel_info_state.toplevel_leave_workspace(&surface, &other_desc.handle); - toplevel_info_state.toplevel_enter_workspace(&surface, &this_desc.handle); + toplevel_leave_workspace(&surface, &other_desc.handle); + toplevel_enter_workspace(&surface, &this_desc.handle); } this_stack.add_window(surface, Some(this_idx + i)); } if this.output != other_output { this_surface.output_leave(&this.output); - toplevel_info_state.toplevel_leave_output(&this_surface, &this.output); - toplevel_info_state.toplevel_enter_output(&this_surface, &other_output); + toplevel_leave_output(this_surface, &this.output); + toplevel_enter_output(this_surface, &other_output); } if this_desc.handle != other_desc.handle { - toplevel_info_state.toplevel_leave_workspace(&this_surface, &this_desc.handle); - toplevel_info_state.toplevel_enter_workspace(&this_surface, &other_desc.handle); + toplevel_leave_workspace(this_surface, &this_desc.handle); + toplevel_enter_workspace(this_surface, &other_desc.handle); } this_stack.remove_window(&this_surface); @@ -1121,24 +1115,23 @@ impl TilingLayout { if this.output != other_output { surface.output_leave(&this.output); surface.output_enter(&other_output, surface.bbox()); - toplevel_info_state.toplevel_leave_output(&surface, &this.output); - toplevel_info_state.toplevel_enter_output(&surface, &other_output); + toplevel_leave_output(&surface, &this.output); + toplevel_enter_output(&surface, &other_output); } if this_desc.handle != other_desc.handle { - toplevel_info_state.toplevel_leave_workspace(&surface, &this_desc.handle); - toplevel_info_state.toplevel_enter_workspace(&surface, &other_desc.handle); + toplevel_leave_workspace(&surface, &this_desc.handle); + toplevel_enter_workspace(&surface, &other_desc.handle); } other_stack.add_window(surface, Some(other_idx + i)); } if this.output != other_output { other_surface.output_leave(&other_output); - toplevel_info_state.toplevel_leave_output(&other_surface, &other_output); - toplevel_info_state.toplevel_enter_output(&other_surface, &this.output); + toplevel_leave_output(other_surface, &other_output); + toplevel_enter_output(other_surface, &this.output); } if this_desc.handle != other_desc.handle { - toplevel_info_state - .toplevel_leave_workspace(&other_surface, &other_desc.handle); - toplevel_info_state.toplevel_enter_workspace(&other_surface, &this_desc.handle); + toplevel_leave_workspace(other_surface, &other_desc.handle); + toplevel_enter_workspace(other_surface, &this_desc.handle); } other_stack.remove_window(&other_surface); @@ -1205,17 +1198,16 @@ impl TilingLayout { other_stack.add_window(this_surface.clone(), Some(other_idx)); if this.output != other_output { - toplevel_info_state.toplevel_leave_output(&this_surface, &this.output); - toplevel_info_state.toplevel_leave_output(&other_surface, &other_output); - toplevel_info_state.toplevel_enter_output(&this_surface, &other_output); - toplevel_info_state.toplevel_enter_output(&other_surface, &this.output); + toplevel_leave_output(this_surface, &this.output); + toplevel_leave_output(other_surface, &other_output); + toplevel_enter_output(this_surface, &other_output); + toplevel_enter_output(other_surface, &this.output); } if this_desc.handle != other_desc.handle { - toplevel_info_state.toplevel_leave_workspace(&this_surface, &this_desc.handle); - toplevel_info_state - .toplevel_leave_workspace(&other_surface, &other_desc.handle); - toplevel_info_state.toplevel_enter_workspace(&this_surface, &other_desc.handle); - toplevel_info_state.toplevel_enter_workspace(&other_surface, &this_desc.handle); + toplevel_leave_workspace(this_surface, &this_desc.handle); + toplevel_leave_workspace(other_surface, &other_desc.handle); + toplevel_enter_workspace(this_surface, &other_desc.handle); + toplevel_enter_workspace(other_surface, &this_desc.handle); } other_stack.remove_window(&other_surface); @@ -1802,7 +1794,7 @@ impl TilingLayout { } pub fn next_focus<'a>( - &mut self, + &self, direction: FocusDirection, seat: &Seat, focus_stack: impl Iterator + 'a, diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 1d76ff18..3abf5532 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1,5 +1,4 @@ use calloop::LoopHandle; -use cosmic::Theme; use indexmap::IndexMap; use std::{ collections::HashMap, @@ -16,32 +15,24 @@ use keyframe::{ease, functions::EaseInOutCubic}; use smithay::{ backend::input::TouchSlot, desktop::{ - layer_map_for_output, space::SpaceElement, LayerSurface, PopupKind, PopupManager, - WindowSurface, WindowSurfaceType, + layer_map_for_output, space::SpaceElement, LayerSurface, PopupKind, WindowSurface, + WindowSurfaceType, }, input::{ - pointer::{Focus, GrabStartData as PointerGrabStartData, MotionEvent}, + pointer::{Focus, GrabStartData as PointerGrabStartData}, Seat, }, output::Output, reexports::{ - wayland_protocols::{ - ext::session_lock::v1::server::ext_session_lock_v1::ExtSessionLockV1, - xdg::shell::server::xdg_toplevel::WmCapabilities, - }, - wayland_server::{protocol::wl_surface::WlSurface, Client, DisplayHandle}, + wayland_protocols::ext::session_lock::v1::server::ext_session_lock_v1::ExtSessionLockV1, + wayland_server::{protocol::wl_surface::WlSurface, Client}, }, - utils::{IsAlive, Logical, Point, Rectangle, Serial, Size, SERIAL_COUNTER}, + utils::{IsAlive, Logical, Point, Rectangle, Serial, Size}, wayland::{ compositor::with_states, seat::WaylandFocus, session_lock::LockSurface, - shell::{ - wlr_layer::{ - KeyboardInteractivity, Layer, LayerSurfaceCachedState, WlrLayerShellState, - }, - xdg::XdgShellState, - }, + shell::wlr_layer::{KeyboardInteractivity, Layer, LayerSurfaceCachedState}, xdg_activation::XdgActivationState, }, xwayland::X11Surface, @@ -50,23 +41,23 @@ use smithay::{ use crate::{ backend::render::animations::spring::{Spring, SpringParams}, config::{Config, KeyModifiers, KeyPattern}, - state::client_should_see_privileged_protocols, utils::prelude::*, wayland::{ handlers::{ - toplevel_management::ToplevelManagementExt, xdg_activation::ActivationContext, + toplevel_management::minimize_rectangle, xdg_activation::ActivationContext, xdg_shell::popup::get_popup_toplevel, }, protocols::{ - toplevel_info::ToplevelInfoState, - toplevel_management::{ManagementCapabilities, ToplevelManagementState}, + toplevel_info::{ + toplevel_enter_output, toplevel_enter_workspace, toplevel_leave_output, + toplevel_leave_workspace, ToplevelInfoState, + }, workspace::{ WorkspaceCapabilities, WorkspaceGroupHandle, WorkspaceHandle, WorkspaceState, WorkspaceUpdateGuard, }, }, }, - xwayland::XWaylandState, }; pub mod element; @@ -196,7 +187,6 @@ impl From<&CosmicSurface> for ActivationKey { pub struct Shell { pub workspaces: Workspaces, - pub popups: PopupManager, pub pending_windows: Vec<(CosmicSurface, Seat, Option)>, pub pending_layers: Vec<(LayerSurface, Output, Seat)>, pub pending_activations: HashMap, @@ -204,15 +194,6 @@ pub struct Shell { pub session_lock: Option, pub seats: Seats, - // wayland_state - pub layer_shell_state: WlrLayerShellState, - pub toplevel_info_state: ToplevelInfoState, - pub toplevel_management_state: ToplevelManagementState, - pub xdg_shell_state: XdgShellState, - pub xdg_activation_state: XdgActivationState, - pub workspace_state: WorkspaceState, - pub xwayland_state: Option, - theme: cosmic::Theme, overview_mode: OverviewMode, swap_indicator: Option, @@ -319,7 +300,6 @@ fn move_workspace_to_group( workspace: &mut Workspace, group: &WorkspaceGroupHandle, workspace_state: &mut WorkspaceUpdateGuard<'_, State>, - toplevel_info_state: &mut ToplevelInfoState, ) { let old_workspace_handle = workspace.handle; workspace.handle = workspace_state @@ -338,14 +318,14 @@ fn move_workspace_to_group( ); for window in workspace.mapped() { for (surface, _) in window.windows() { - toplevel_info_state.toplevel_leave_workspace(&surface, &old_workspace_handle); - toplevel_info_state.toplevel_enter_workspace(&surface, &workspace.handle); + toplevel_leave_workspace(&surface, &old_workspace_handle); + toplevel_enter_workspace(&surface, &workspace.handle); } } for window in workspace.minimized_windows.iter() { for (surface, _) in window.window.windows() { - toplevel_info_state.toplevel_leave_workspace(&surface, &old_workspace_handle); - toplevel_info_state.toplevel_enter_workspace(&surface, &workspace.handle); + toplevel_leave_workspace(&surface, &old_workspace_handle); + toplevel_enter_workspace(&surface, &workspace.handle); } } workspace_state.remove_workspace(old_workspace_handle); @@ -464,18 +444,14 @@ impl WorkspaceSet { } } - fn set_output( - &mut self, - new_output: &Output, - toplevel_info: &mut ToplevelInfoState, - ) { + fn set_output(&mut self, new_output: &Output) { self.sticky_layer.set_output(new_output); for window in self.sticky_layer.windows() { - toplevel_info.toplevel_leave_output(&window, &self.output); - toplevel_info.toplevel_enter_output(&window, &new_output); + toplevel_leave_output(&window, &self.output); + toplevel_enter_output(&window, &new_output); } for workspace in &mut self.workspaces { - workspace.set_output(new_output, toplevel_info); + workspace.set_output(new_output); } self.output = new_output.clone(); } @@ -593,7 +569,6 @@ impl Workspaces { &mut self, output: &Output, workspace_state: &mut WorkspaceUpdateGuard<'_, State>, - toplevel_info_state: &mut ToplevelInfoState, xdg_activation_state: &XdgActivationState, ) { if self.sets.contains_key(output) { @@ -604,7 +579,7 @@ impl Workspaces { .backup_set .take() .map(|mut set| { - set.set_output(output, toplevel_info_state); + set.set_output(output); set }) .unwrap_or_else(|| { @@ -635,16 +610,11 @@ impl Workspaces { { let set = self.sets.get_mut(output).unwrap(); for workspace in &mut moved_workspaces { - move_workspace_to_group( - workspace, - &set.group, - workspace_state, - toplevel_info_state, - ); + move_workspace_to_group(workspace, &set.group, workspace_state); } set.workspaces.extend(moved_workspaces); for (i, workspace) in set.workspaces.iter_mut().enumerate() { - workspace.set_output(output, toplevel_info_state); + workspace.set_output(output); workspace.refresh(xdg_activation_state); workspace_set_idx(workspace_state, i as u8 + 1, set.idx, &workspace.handle); if i == set.active { @@ -659,7 +629,6 @@ impl Workspaces { output: &Output, seats: impl Iterator>, workspace_state: &mut WorkspaceUpdateGuard<'_, State>, - toplevel_info_state: &mut ToplevelInfoState, xdg_activation_state: &XdgActivationState, ) { if !self.sets.contains_key(output) { @@ -689,30 +658,25 @@ impl Workspaces { let workspace_group = new_set.group; for mut workspace in set.workspaces.into_iter() { // update workspace protocol state - move_workspace_to_group( - &mut workspace, - &workspace_group, - workspace_state, - toplevel_info_state, - ); + move_workspace_to_group(&mut workspace, &workspace_group, workspace_state); // update mapping - workspace.set_output(&new_output, toplevel_info_state); + workspace.set_output(&new_output); workspace.refresh(xdg_activation_state); new_set.workspaces.push(workspace); } for window in set.sticky_layer.mapped() { for (surface, _) in window.windows() { - toplevel_info_state.toplevel_leave_output(&surface, output); - toplevel_info_state.toplevel_enter_output(&surface, &new_output); + toplevel_leave_output(&surface, output); + toplevel_enter_output(&surface, &new_output); } } new_set.sticky_layer.merge(set.sticky_layer); for window in set.minimized_windows.iter() { for (surface, _) in window.window.windows() { - toplevel_info_state.toplevel_leave_output(&surface, output); - toplevel_info_state.toplevel_enter_output(&surface, &new_output); + toplevel_leave_output(&surface, output); + toplevel_enter_output(&surface, &new_output); } } new_set.minimized_windows.extend(set.minimized_windows); @@ -741,7 +705,6 @@ impl Workspaces { to: &Output, handle: &WorkspaceHandle, workspace_state: &mut WorkspaceUpdateGuard<'_, State>, - toplevel_info_state: &mut ToplevelInfoState, xdg_activation_state: &XdgActivationState, ) { if !self.sets.contains_key(to) { @@ -753,13 +716,8 @@ impl Workspaces { Some(set.workspaces.remove(pos)) }) { let new_set = self.sets.get_mut(to).unwrap(); - move_workspace_to_group( - &mut workspace, - &new_set.group, - workspace_state, - toplevel_info_state, - ); - workspace.set_output(to, toplevel_info_state); + move_workspace_to_group(&mut workspace, &new_set.group, workspace_state); + workspace.set_output(to); workspace.refresh(xdg_activation_state); new_set.workspaces.insert(new_set.active + 1, workspace) } @@ -1010,8 +968,14 @@ impl Workspaces { pub fn set_theme(&mut self, theme: cosmic::Theme, xdg_activation_state: &XdgActivationState) { for (_, s) in &mut self.sets { s.theme = theme.clone(); + s.sticky_layer.theme = theme.clone(); + s.sticky_layer.mapped().for_each(|m| { + m.update_theme(theme.clone()); + m.force_redraw(); + }); s.sticky_layer.refresh(); + for w in &mut s.workspaces { w.tiling_layer.theme = theme.clone(); w.floating_layer.theme = theme.clone(); @@ -1028,7 +992,7 @@ impl Workspaces { } } - fn update_autotile_behavior<'a>( + pub fn update_autotile_behavior<'a>( &mut self, behavior: TileBehavior, guard: &mut WorkspaceUpdateGuard<'_, State>, @@ -1061,7 +1025,7 @@ impl Workspaces { } } - fn update_autotile<'a>( + pub fn update_autotile<'a>( &mut self, autotile: bool, guard: &mut WorkspaceUpdateGuard<'_, State>, @@ -1075,84 +1039,30 @@ impl Workspaces { #[derive(Debug)] pub struct InvalidWorkspaceIndex; -impl Shell { - pub fn new(config: &Config, dh: &DisplayHandle) -> Self { - let layer_shell_state = WlrLayerShellState::new_with_filter::( - dh, - client_should_see_privileged_protocols, - ); - let xdg_shell_state = XdgShellState::new_with_capabilities::( - dh, - [ - WmCapabilities::Fullscreen, - WmCapabilities::Maximize, - WmCapabilities::Minimize, - WmCapabilities::WindowMenu, - ], - ); - let xdg_activation_state = XdgActivationState::new::(dh); - let toplevel_info_state = - ToplevelInfoState::new(dh, client_should_see_privileged_protocols); - let toplevel_management_state = ToplevelManagementState::new::( - dh, - vec![ - ManagementCapabilities::Close, - ManagementCapabilities::Activate, - ManagementCapabilities::Maximize, - ManagementCapabilities::Minimize, - ManagementCapabilities::MoveToWorkspace, - ], - client_should_see_privileged_protocols, - ); - let workspace_state = WorkspaceState::new(dh, client_should_see_privileged_protocols); - let theme = cosmic::theme::system_preference(); - - Shell { - popups: PopupManager::default(), - workspaces: Workspaces::new(config, theme.clone()), - seats: Seats::new(), - - pending_windows: Vec::new(), - pending_layers: Vec::new(), - pending_activations: HashMap::new(), - override_redirect_windows: Vec::new(), - session_lock: None, - - layer_shell_state, - toplevel_info_state, - toplevel_management_state, - xdg_shell_state, - xdg_activation_state, - workspace_state, - xwayland_state: None, - - theme, - overview_mode: OverviewMode::None, - swap_indicator: None, - resize_mode: ResizeMode::None, - resize_state: None, - resize_indicator: None, - } - } - +impl Common { pub fn add_output(&mut self, output: &Output) { - self.workspaces.add_output( + let mut shell = self.shell.write().unwrap(); + shell.workspaces.add_output( output, &mut self.workspace_state.update(), - &mut self.toplevel_info_state, &self.xdg_activation_state, ); + + std::mem::drop(shell); self.refresh(); // fixes indicies of any moved workspaces } pub fn remove_output(&mut self, output: &Output) { - self.workspaces.remove_output( + let mut shell = self.shell.write().unwrap(); + let shell_ref = &mut *shell; + shell_ref.workspaces.remove_output( output, - self.seats.iter(), + shell_ref.seats.iter(), &mut self.workspace_state.update(), - &mut self.toplevel_info_state, &self.xdg_activation_state, ); + + std::mem::drop(shell); self.refresh(); // cleans up excess of workspaces and empty workspaces } @@ -1161,21 +1071,71 @@ impl Shell { return; } - self.workspaces.migrate_workspace( + let mut shell = self.shell.write().unwrap(); + shell.workspaces.migrate_workspace( from, to, handle, &mut self.workspace_state.update(), - &mut self.toplevel_info_state, &self.xdg_activation_state, ); + + std::mem::drop(shell); self.refresh(); // fixes index of moved workspace } - pub fn update_config(&mut self, config: &Config) { + pub fn update_config(&mut self) { + let mut shell = self.shell.write().unwrap(); let mut workspace_state = self.workspace_state.update(); - self.workspaces - .update_config(config, &mut workspace_state, &self.xdg_activation_state); + shell.workspaces.update_config( + &self.config, + &mut workspace_state, + &self.xdg_activation_state, + ); + } + + #[profiling::function] + pub fn refresh(&mut self) { + self.xdg_activation_state.retain_tokens(|_, data| { + Instant::now().duration_since(data.timestamp) < Duration::from_secs(5) + }); + self.shell.write().unwrap().refresh( + &self.xdg_activation_state, + &mut self.workspace_state.update(), + ); + self.popups.cleanup(); + self.toplevel_info_state.refresh(&self.workspace_state); + } + + pub fn on_commit(&mut self, surface: &WlSurface) { + if let Some(mapped) = self.shell.read().unwrap().element_for_surface(surface) { + mapped.on_commit(surface); + } + self.popups.commit(surface); + } +} + +impl Shell { + pub fn new(config: &Config) -> Self { + let theme = cosmic::theme::system_preference(); + + Shell { + workspaces: Workspaces::new(config, theme.clone()), + seats: Seats::new(), + + pending_windows: Vec::new(), + pending_layers: Vec::new(), + pending_activations: HashMap::new(), + override_redirect_windows: Vec::new(), + session_lock: None, + + theme, + overview_mode: OverviewMode::None, + swap_indicator: None, + resize_mode: ResizeMode::None, + resize_state: None, + resize_indicator: None, + } } pub fn activate( @@ -1183,6 +1143,7 @@ impl Shell { output: &Output, idx: usize, workspace_delta: WorkspaceDelta, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, ) -> Result>, InvalidWorkspaceIndex> { match &mut self.workspaces.mode { WorkspaceMode::OutputBound => { @@ -1193,7 +1154,7 @@ impl Shell { ) { set.workspaces[set.active].tiling_layer.cleanup_drag(); } - set.activate(idx, workspace_delta, &mut self.workspace_state.update())?; + set.activate(idx, workspace_delta, workspace_state)?; let output_geo = output.geometry(); Ok(Some( @@ -1206,7 +1167,7 @@ impl Shell { } WorkspaceMode::Global => { for set in self.workspaces.sets.values_mut() { - set.activate(idx, workspace_delta, &mut self.workspace_state.update())?; + set.activate(idx, workspace_delta, workspace_state)?; } Ok(None) } @@ -1232,6 +1193,7 @@ impl Shell { &mut self, output: &Output, velocity: f64, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, ) -> Result>, InvalidWorkspaceIndex> { match &mut self.workspaces.mode { WorkspaceMode::OutputBound => { @@ -1255,7 +1217,7 @@ impl Shell { delta.abs(), velocity.abs(), ), - &mut self.workspace_state.update(), + workspace_state, )?; } else { set.activate_previous( @@ -1263,46 +1225,13 @@ impl Shell { 1.0 - delta.abs(), velocity.abs(), ), - &mut self.workspace_state.update(), + workspace_state, )?; } } _ => {} // Do nothing } } - if let Some(xwm) = self - .xwayland_state - .as_mut() - .and_then(|state| state.xwm.as_mut()) - { - { - for window in set.workspaces[set.active] - .tiling_layer - .mapped() - .map(|(w, _)| w) - .chain(set.workspaces[set.active].floating_layer.space.elements()) - { - if let Some(surf) = window.active_window().x11_surface() { - let _ = xwm.raise_window(surf); - } - } - for window in set.sticky_layer.space.elements() { - if let Some(surf) = window.active_window().x11_surface() { - let _ = xwm.raise_window(surf); - } - } - if let Some(surf) = set.workspaces[set.active] - .fullscreen - .as_ref() - .and_then(|f| f.surface.x11_surface()) - { - let _ = xwm.raise_window(surf); - } - } - for surface in &self.override_redirect_windows { - let _ = xwm.raise_window(surface); - } - } let output_geo = output.geometry(); Ok(Some( @@ -1328,7 +1257,7 @@ impl Shell { delta.abs(), velocity.abs(), ), - &mut self.workspace_state.update(), + workspace_state, )?; } else { set.activate_previous( @@ -1336,7 +1265,7 @@ impl Shell { 1.0 - delta.abs(), velocity.abs(), ), - &mut self.workspace_state.update(), + workspace_state, )?; } } @@ -1357,10 +1286,14 @@ impl Shell { self.workspaces.active_mut(output) } - pub fn refresh_active_space(&mut self, output: &Output) { + pub fn refresh_active_space( + &mut self, + output: &Output, + xdg_activation_state: &XdgActivationState, + ) { self.workspaces .active_mut(output) - .refresh(&self.xdg_activation_state) + .refresh(xdg_activation_state) } pub fn visible_output_for_surface(&self, surface: &WlSurface) -> Option<&Output> { @@ -1675,8 +1608,11 @@ impl Shell { } } - #[profiling::function] - pub fn refresh(&mut self) { + fn refresh( + &mut self, + xdg_activation_state: &XdgActivationState, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, + ) { if let OverviewMode::Ended(_, timestamp) = self.overview_mode { if Instant::now().duration_since(timestamp) > ANIMATION_DURATION { self.overview_mode = OverviewMode::None; @@ -1690,15 +1626,8 @@ impl Shell { } } - self.popups.cleanup(); - - self.xdg_activation_state.retain_tokens(|_, data| { - Instant::now().duration_since(data.timestamp) < Duration::from_secs(5) - }); - self.workspaces.refresh( - &mut self.workspace_state.update(), - &self.xdg_activation_state, - ); + self.workspaces + .refresh(workspace_state, xdg_activation_state); for output in self.outputs() { let mut map = layer_map_for_output(output); @@ -1712,15 +1641,6 @@ impl Shell { self.pending_layers.retain(|(s, _, _)| s.alive()); self.pending_windows.retain(|(s, _, _)| s.alive()); - - self.toplevel_info_state.refresh(&self.workspace_state); - } - - pub fn on_commit(&mut self, surface: &WlSurface) { - if let Some(mapped) = self.element_for_surface(surface) { - mapped.on_commit(surface); - } - self.popups.commit(surface); } pub fn remap_unfullscreened_window( @@ -1752,10 +1672,8 @@ impl Shell { .output() .clone(); for (window, _) in mapped.windows() { - self.toplevel_info_state - .toplevel_enter_output(&window, &new_workspace_output); - self.toplevel_info_state - .toplevel_enter_workspace(&window, &previous_workspace); + toplevel_enter_output(&window, &new_workspace_output); + toplevel_enter_workspace(&window, &previous_workspace); } let new_workspace = self @@ -1785,8 +1703,9 @@ impl Shell { pub fn map_window( &mut self, window: &CosmicSurface, + toplevel_info: &mut ToplevelInfoState, + workspace_state: &mut WorkspaceState, evlh: &LoopHandle<'static, State>, - theme: &Theme, ) -> Option { let pos = self .pending_windows @@ -1864,11 +1783,11 @@ impl Shell { self.workspaces.active_mut(&output) }; - self.toplevel_info_state.new_toplevel(&window, &self.workspace_state); - self.toplevel_info_state - .toplevel_enter_output(&window, &output); - self.toplevel_info_state - .toplevel_enter_workspace(&window, &workspace.handle); + toplevel_info.new_toplevel(&window, workspace_state); + toplevel_enter_output(&window, &output); + toplevel_enter_workspace(&window, &workspace.handle); + + let mut workspace_state = workspace_state.update(); let workspace_output = workspace.output.clone(); let was_activated = workspace_handle.is_some() @@ -1884,7 +1803,7 @@ impl Shell { { focused.stack_ref().unwrap().add_window(window, None); if was_activated { - self.set_urgent(&workspace_handle); + workspace_state.add_workspace_state(&workspace_handle, WState::Urgent); } return None; } @@ -1893,7 +1812,7 @@ impl Shell { let mapped = CosmicMapped::from(CosmicWindow::new( window.clone(), evlh.clone(), - theme.clone(), + self.theme.clone(), )); #[cfg(feature = "debug")] { @@ -1920,9 +1839,7 @@ impl Shell { } if !parent_is_sticky && should_be_fullscreen { - let from = self - .toplevel_management_state - .minimize_rectangle(&output, &mapped.active_window()); + let from = minimize_rectangle(&output, &mapped.active_window()); workspace.fullscreen_request(&mapped.active_window(), None, from, &seat); } @@ -1940,7 +1857,7 @@ impl Shell { } else { if workspace_empty || was_activated || should_be_fullscreen { self.append_focus_stack(&mapped, &seat); - self.set_urgent(&workspace_handle); + workspace_state.add_workspace_state(&workspace_handle, WState::Urgent); } None }; @@ -1995,8 +1912,12 @@ impl Shell { wants_focus.then(|| layer_surface.into()) } - pub fn unmap_surface(&mut self, surface: &S, seat: &Seat) - where + pub fn unmap_surface( + &mut self, + surface: &S, + seat: &Seat, + toplevel_info: &mut ToplevelInfoState, + ) where CosmicSurface: PartialEq, { for set in self.workspaces.sets.values_mut() { @@ -2044,7 +1965,7 @@ impl Shell { }; if let Some(surface) = surface { - self.toplevel_info_state.remove_toplevel(&surface); + toplevel_info.remove_toplevel(&surface); self.pending_windows.push((surface, seat.clone(), None)); return; } @@ -2087,6 +2008,7 @@ impl Shell { to: &WorkspaceHandle, follow: bool, direction: Option, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, ) -> 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(); @@ -2096,11 +2018,9 @@ impl Shell { let elements = from_workspace.mapped().cloned().collect::>(); for (toplevel, _) in mapped.windows() { - self.toplevel_info_state - .toplevel_leave_workspace(&toplevel, from); + toplevel_leave_workspace(&toplevel, from); if from_output != to_output { - self.toplevel_info_state - .toplevel_leave_output(&toplevel, &from_output); + toplevel_leave_output(&toplevel, &from_output); } } for mapped in elements.into_iter() { @@ -2113,8 +2033,13 @@ impl Shell { self.workspaces .idx_for_handle(&to_output, to) .and_then(|to_idx| { - self.activate(&to_output, to_idx, WorkspaceDelta::new_shortcut()) - .unwrap() + self.activate( + &to_output, + to_idx, + WorkspaceDelta::new_shortcut(), + workspace_state, + ) + .unwrap() }) } else { None @@ -2156,9 +2081,7 @@ impl Shell { } } - let from = self - .toplevel_management_state - .minimize_rectangle(&to_output, &mapped.active_window()); + let from = minimize_rectangle(&to_output, &mapped.active_window()); to_workspace.fullscreen_request(&mapped.active_window(), f.previously, from, &any_seat); to_workspace @@ -2180,11 +2103,9 @@ impl Shell { } for (toplevel, _) in mapped.windows() { if from_output != to_output { - self.toplevel_info_state - .toplevel_enter_output(&toplevel, &to_output); + toplevel_enter_output(&toplevel, &to_output); } - self.toplevel_info_state - .toplevel_enter_workspace(&toplevel, to); + toplevel_enter_workspace(&toplevel, to); } new_pos.map(|pos| (focus_target, pos)) @@ -2198,6 +2119,7 @@ impl Shell { to: (&Output, Option), follow: bool, direction: Option, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, ) -> Result)>, InvalidWorkspaceIndex> { let (to_output, to_idx) = to; let to_idx = to_idx.unwrap_or(self.workspaces.active_num(to_output).1); @@ -2221,7 +2143,15 @@ impl Shell { let from = from_workspace.handle; - Ok(self.move_window(Some(seat), &mapped, &from, &to, follow, direction)) + Ok(self.move_window( + Some(seat), + &mapped, + &from, + &to, + follow, + direction, + workspace_state, + )) } pub fn update_reactive_popups(&self, mapped: &CosmicMapped) { @@ -2245,350 +2175,296 @@ impl Shell { } pub fn menu_request( - state: &mut State, + &self, surface: &WlSurface, seat: &Seat, serial: impl Into>, location: Point, target_stack: bool, - ) { + config: &Config, + evlh: &LoopHandle<'static, State>, + ) -> Option<(MenuGrab, Focus)> { let serial = serial.into(); - if let Some(GrabStartData::Pointer(start_data)) = + let Some(GrabStartData::Pointer(start_data)) = check_grab_preconditions(&seat, surface, serial, ReleaseMode::NoMouseButtons) - { - if let Some(mapped) = state.common.shell.element_for_surface(surface).cloned() { - let (_, relative_loc) = mapped - .windows() - .find(|(w, _)| w.wl_surface().as_ref() == Some(surface)) - .unwrap(); + else { + return None; + }; - let (global_position, edge, is_tiled, is_stacked, is_sticky, tiling_enabled) = - if let Some(set) = state - .common - .shell - .workspaces - .sets - .values_mut() - .find(|set| set.sticky_layer.mapped().any(|m| m == &mapped)) - { - let output = set.output.clone(); - let global_position = - (set.sticky_layer.element_geometry(&mapped).unwrap().loc - + relative_loc.as_local() - + location.as_local()) - .to_global(&output); - ( - global_position, - ResizeEdge::all(), - false, - mapped.is_stack(), - true, - false, - ) - } else if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { - let output = seat.active_output(); - - let Some(elem_geo) = workspace.element_geometry(&mapped) else { - return; - }; - let global_position = - (elem_geo.loc + relative_loc.as_local() + location.as_local()) - .to_global(&output); - let is_tiled = workspace.is_tiled(&mapped); - let edge = if is_tiled { - mapped - .tiling_node_id - .lock() - .unwrap() - .clone() - .map(|node_id| { - TilingLayout::possible_resizes( - workspace.tiling_layer.tree(), - node_id, - ) - }) - .unwrap_or(ResizeEdge::empty()) - } else { - ResizeEdge::all() - }; + let mapped = self.element_for_surface(surface).cloned()?; + let (_, relative_loc) = mapped + .windows() + .find(|(w, _)| w.wl_surface().as_ref() == Some(surface)) + .unwrap(); - ( - global_position, - edge, - is_tiled, - mapped.is_stack(), - false, - workspace.tiling_enabled, - ) - } else { - return; - }; + let (global_position, edge, is_tiled, is_stacked, is_sticky, tiling_enabled) = + if let Some(set) = self + .workspaces + .sets + .values() + .find(|set| set.sticky_layer.mapped().any(|m| m == &mapped)) + { + let output = set.output.clone(); + let global_position = (set.sticky_layer.element_geometry(&mapped).unwrap().loc + + relative_loc.as_local() + + location.as_local()) + .to_global(&output); + ( + global_position, + ResizeEdge::all(), + false, + mapped.is_stack(), + true, + false, + ) + } else if let Some(workspace) = self.space_for(&mapped) { + let output = seat.active_output(); + + let elem_geo = workspace.element_geometry(&mapped)?; + let global_position = + (elem_geo.loc + relative_loc.as_local() + location.as_local()) + .to_global(&output); + let is_tiled = workspace.is_tiled(&mapped); + let edge = if is_tiled { + mapped + .tiling_node_id + .lock() + .unwrap() + .clone() + .map(|node_id| { + TilingLayout::possible_resizes(workspace.tiling_layer.tree(), node_id) + }) + .unwrap_or(ResizeEdge::empty()) + } else { + ResizeEdge::all() + }; - let grab = MenuGrab::new( - start_data, - seat, - if target_stack || !is_stacked { - Box::new(window_items( - &mapped, - is_tiled, - is_stacked, - is_sticky, - tiling_enabled, - edge, - &state.common.config.static_conf, - )) as Box> - } else { - let (tab, _) = mapped - .windows() - .find(|(s, _)| s.wl_surface().as_ref() == Some(surface)) - .unwrap(); - Box::new(tab_items( - &mapped, - &tab, - is_tiled, - &state.common.config.static_conf, - )) as Box> - }, + ( global_position, - state.common.event_loop_handle.clone(), - state.common.theme.clone(), - ); - seat.get_pointer().unwrap().set_grab( - state, - grab, - serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()), - Focus::Keep, - ); - } - } + edge, + is_tiled, + mapped.is_stack(), + false, + workspace.tiling_enabled, + ) + } else { + return None; + }; + + let grab = MenuGrab::new( + start_data, + seat, + if target_stack || !is_stacked { + Box::new(window_items( + &mapped, + is_tiled, + is_stacked, + is_sticky, + tiling_enabled, + edge, + &config.static_conf, + )) as Box> + } else { + let (tab, _) = mapped + .windows() + .find(|(s, _)| s.wl_surface().as_ref() == Some(surface)) + .unwrap(); + Box::new(tab_items(&mapped, &tab, is_tiled, &config.static_conf)) + as Box> + }, + global_position, + evlh.clone(), + self.theme.clone(), + ); + + Some((grab, Focus::Keep)) } pub fn move_request( - state: &mut State, + &mut self, surface: &WlSurface, seat: &Seat, serial: impl Into>, release: ReleaseMode, move_out_of_stack: bool, - ) { + config: &Config, + evlh: &LoopHandle<'static, State>, + xdg_activation_state: &XdgActivationState, + ) -> Option<(MoveGrab, Focus)> { let serial = serial.into(); - if let Some(mut start_data) = check_grab_preconditions(&seat, surface, serial, release) { - if let Some(mut old_mapped) = state.common.shell.element_for_surface(surface).cloned() { - if old_mapped.is_minimized() { - return; - } + let mut start_data = check_grab_preconditions(&seat, surface, serial, release)?; + let mut old_mapped = self.element_for_surface(surface).cloned()?; + if old_mapped.is_minimized() { + return None; + } - for workspace in state.common.shell.workspaces.spaces_mut() { - for seat in state.common.shell.seats.iter() { - let mut stack = workspace.focus_stack.get_mut(seat); - stack.remove(&old_mapped); - } - } + for workspace in self.workspaces.spaces_mut() { + for seat in self.seats.iter() { + let mut stack = workspace.focus_stack.get_mut(seat); + stack.remove(&old_mapped); + } + } - let (window, _) = old_mapped - .windows() - .find(|(w, _)| w.wl_surface().as_ref() == Some(surface)) - .unwrap(); + let (window, _) = old_mapped + .windows() + .find(|(w, _)| w.wl_surface().as_ref() == Some(surface)) + .unwrap(); + + let mapped = if move_out_of_stack { + let new_mapped: CosmicMapped = + CosmicWindow::new(window.clone(), evlh.clone(), self.theme.clone()).into(); + start_data.set_focus(new_mapped.focus_under((0., 0.).into())); + new_mapped + } else { + old_mapped.clone() + }; - let mapped = if move_out_of_stack { - let new_mapped: CosmicMapped = CosmicWindow::new( - window.clone(), - state.common.event_loop_handle.clone(), - state.common.theme.clone(), + let trigger = match &start_data { + GrabStartData::Pointer(start_data) => Trigger::Pointer(start_data.button), + GrabStartData::Touch(start_data) => Trigger::Touch(start_data.slot), + }; + let active_hint = if config.cosmic_conf.active_hint { + self.theme.cosmic().active_hint as u8 + } else { + 0 + }; + let pointer = seat.get_pointer().unwrap(); + let pos = pointer.current_location().as_global(); + + let cursor_output = if let Some(output) = + self.outputs() + .find(|output| { + output.geometry().as_logical().overlaps_or_touches( + Rectangle::from_loc_and_size(start_data.location().to_i32_floor(), (0, 0)), ) - .into(); - start_data.set_focus(new_mapped.focus_under((0., 0.).into())); - new_mapped - } else { - old_mapped.clone() - }; + }) + .cloned() + { + output + } else { + seat.active_output() + }; - let trigger = match &start_data { - GrabStartData::Pointer(start_data) => Trigger::Pointer(start_data.button), - GrabStartData::Touch(start_data) => Trigger::Touch(start_data.slot), - }; - let active_hint = if state.common.config.cosmic_conf.active_hint { - state.common.theme.cosmic().active_hint as u8 - } else { - 0 - }; - let pos = start_data.location().as_global(); - - let cursor_output = if let Some(output) = state - .common - .shell - .outputs() - .find(|output| { - output.geometry().as_logical().overlaps_or_touches( - Rectangle::from_loc_and_size( - start_data.location().to_i32_floor(), - (0, 0), - ), - ) - }) - .cloned() - { - output - } else { - seat.active_output() - }; + let (initial_window_location, layer, workspace_handle) = if let Some(workspace) = + self.space_for_mut(&old_mapped) + { + if workspace + .fullscreen + .as_ref() + .is_some_and(|f| f.surface == window) + { + let _ = workspace.remove_fullscreen(); // We are moving this window, we don't need to send it back to it's original workspace + } - let (initial_window_location, layer, workspace_handle) = if let Some(workspace) = - state.common.shell.space_for_mut(&old_mapped) - { - if workspace - .fullscreen - .as_ref() - .is_some_and(|f| f.surface == window) - { - let _ = workspace.remove_fullscreen(); // We are moving this window, we don't need to send it back to it's original workspace - } + let elem_geo = workspace.element_geometry(&old_mapped)?; + let mut initial_window_location = elem_geo.loc.to_global(workspace.output()); - let Some(elem_geo) = workspace.element_geometry(&old_mapped) else { - return; - }; - let mut initial_window_location = elem_geo.loc.to_global(&workspace.output()); - - if mapped.maximized_state.lock().unwrap().is_some() { - // If surface is maximized then unmaximize it - let new_size = workspace.unmaximize_request(&mapped); - let output = workspace.output(); - let ratio = pos.to_local(output).x / output.geometry().size.w as f64; - - initial_window_location = new_size - .map(|size| (pos.x - (size.w as f64 * ratio), pos.y).into()) - .unwrap_or_else(|| pos) - .to_i32_round(); - } + if mapped.maximized_state.lock().unwrap().is_some() { + // If surface is maximized then unmaximize it + let new_size = workspace.unmaximize_request(&mapped); + let output = workspace.output(); + let ratio = pos.to_local(&output).x / output.geometry().size.w as f64; - let layer = if mapped == old_mapped { - let was_floating = workspace.floating_layer.unmap(&mapped); - let was_tiled = workspace.tiling_layer.unmap_as_placeholder(&mapped); - assert!(was_floating != was_tiled.is_some()); - was_tiled.is_some() - } else { - workspace - .tiling_layer - .mapped() - .any(|(m, _)| m == &old_mapped) - } - .then_some(ManagedLayer::Tiling) - .unwrap_or(ManagedLayer::Floating); + initial_window_location = new_size + .map(|size| (pos.x - (size.w as f64 * ratio), pos.y).into()) + .unwrap_or_else(|| pos) + .to_i32_round(); + } - (initial_window_location, layer, workspace.handle) - } else if let Some(sticky_layer) = state - .common - .shell - .workspaces - .sets - .get_mut(&cursor_output) - .filter(|set| set.sticky_layer.mapped().any(|m| m == &old_mapped)) - .map(|set| &mut set.sticky_layer) - { - let mut initial_window_location = sticky_layer - .element_geometry(&old_mapped) - .unwrap() - .loc - .to_global(&cursor_output); - - if let Some(state) = mapped.maximized_state.lock().unwrap().take() { - // If surface is maximized then unmaximize it - mapped.set_maximized(false); - let new_size = state.original_geometry.size.as_logical(); - sticky_layer.map_internal( - mapped.clone(), - Some(state.original_geometry.loc), - Some(new_size), - None, - ); + let layer = if mapped == old_mapped { + let was_floating = workspace.floating_layer.unmap(&mapped); + let was_tiled = workspace.tiling_layer.unmap_as_placeholder(&mapped); + assert!(was_floating != was_tiled.is_some()); + was_tiled.is_some() + } else { + workspace + .tiling_layer + .mapped() + .any(|(m, _)| m == &old_mapped) + } + .then_some(ManagedLayer::Tiling) + .unwrap_or(ManagedLayer::Floating); - let ratio = - pos.to_local(&cursor_output).x / cursor_output.geometry().size.w as f64; - initial_window_location = - Point::::from((pos.x - (new_size.w as f64 * ratio), pos.y)) - .to_i32_round(); - } + (initial_window_location, layer, workspace.handle) + } else if let Some(sticky_layer) = self + .workspaces + .sets + .get_mut(&cursor_output) + .filter(|set| set.sticky_layer.mapped().any(|m| m == &old_mapped)) + .map(|set| &mut set.sticky_layer) + { + let mut initial_window_location = sticky_layer + .element_geometry(&old_mapped) + .unwrap() + .loc + .to_global(&cursor_output); + + if let Some(state) = mapped.maximized_state.lock().unwrap().take() { + // If surface is maximized then unmaximize it + mapped.set_maximized(false); + let new_size = state.original_geometry.size.as_logical(); + sticky_layer.map_internal( + mapped.clone(), + Some(state.original_geometry.loc), + Some(new_size), + None, + ); - if mapped == old_mapped { - sticky_layer.unmap(&mapped); - } + let ratio = pos.to_local(&cursor_output).x / cursor_output.geometry().size.w as f64; + initial_window_location = + Point::::from((pos.x - (new_size.w as f64 * ratio), pos.y)) + .to_i32_round(); + } - ( - initial_window_location, - ManagedLayer::Sticky, - state.common.shell.active_space(&cursor_output).handle, - ) - } else { - return; - }; + if mapped == old_mapped { + sticky_layer.unmap(&mapped); + } - state - .common - .shell - .toplevel_info_state - .toplevel_leave_workspace(&window, &workspace_handle); - state - .common - .shell - .toplevel_info_state - .toplevel_leave_output(&window, &cursor_output); - - if move_out_of_stack { - old_mapped.stack_ref_mut().unwrap().remove_window(&window); - state - .common - .shell - .workspaces - .space_for_handle_mut(&workspace_handle) - .unwrap() - .refresh(&state.common.shell.xdg_activation_state); - } + ( + initial_window_location, + ManagedLayer::Sticky, + self.active_space(&cursor_output).handle, + ) + } else { + return None; + }; - let is_touch_grab = matches!(start_data, GrabStartData::Touch(_)); - - let grab = MoveGrab::new( - start_data, - mapped, - seat, - initial_window_location, - cursor_output, - active_hint as u8, - layer, - release, - state.common.event_loop_handle.clone(), - ); + toplevel_leave_workspace(&window, &workspace_handle); + toplevel_leave_output(&window, &cursor_output); - if grab.is_tiling_grab() { - state - .common - .shell - .set_overview_mode(Some(trigger), state.common.event_loop_handle.clone()); - } + if move_out_of_stack { + old_mapped.stack_ref_mut().unwrap().remove_window(&window); + self.workspaces + .space_for_handle_mut(&workspace_handle) + .unwrap() + .refresh(xdg_activation_state); + } + + let grab = MoveGrab::new( + start_data, + mapped, + seat, + initial_window_location, + cursor_output, + active_hint as u8, + layer, + release, + evlh.clone(), + ); - if is_touch_grab { - seat.get_touch().unwrap().set_grab( - state, - grab, - serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()), - ); - } else { - seat.get_pointer().unwrap().set_grab( - state, - grab, - serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()), - Focus::Clear, - ); - } - } + if grab.is_tiling_grab() { + self.set_overview_mode(Some(trigger), evlh.clone()); } + + Some((grab, Focus::Clear)) } #[must_use] - pub fn next_focus<'a>(&mut self, direction: FocusDirection, seat: &Seat) -> FocusResult { + pub fn next_focus<'a>(&self, direction: FocusDirection, seat: &Seat) -> FocusResult { let overview = self.overview_mode().0; let output = seat.active_output(); - let workspace = self.active_space_mut(&output); + let workspace = self.active_space(&output); if workspace.fullscreen.is_some() { return FocusResult::None; @@ -2598,9 +2474,9 @@ impl Shell { return FocusResult::None; }; - let set = self.workspaces.sets.get_mut(&output).unwrap(); - let sticky_layer = &mut set.sticky_layer; - let workspace = &mut set.workspaces[set.active]; + let set = self.workspaces.sets.get(&output).unwrap(); + let sticky_layer = &set.sticky_layer; + let workspace = &set.workspaces[set.active]; let Some(focused) = (match target { KeyboardFocusTarget::Popup(popup) => { @@ -2642,7 +2518,7 @@ impl Shell { .tiling_layer .next_focus(direction, seat, focus_stack.iter(), swap_desc) } else { - let floating_layer = &mut set.workspaces[set.active].floating_layer; + let floating_layer = &set.workspaces[set.active].floating_layer; let geometry = sticky_layer .space @@ -2765,121 +2641,97 @@ impl Shell { } pub fn menu_resize_request( - state: &mut State, + &mut self, mapped: &CosmicMapped, seat: &Seat, edge: ResizeEdge, - ) { - let Some(surface) = mapped.active_window().wl_surface() else { - return; - }; + ) -> Option<( + ( + Option<(PointerFocusTarget, Point)>, + Point, + ), + (ResizeGrab, Focus), + )> { + let surface = mapped.active_window().wl_surface()?; if mapped.is_fullscreen(true) || mapped.is_maximized(true) { - return; + return None; } - if let Some(mut start_data) = - check_grab_preconditions(&seat, &surface, None, ReleaseMode::Click) - { - let (floating_layer, geometry) = if let Some(set) = state - .common - .shell - .workspaces - .sets - .values_mut() - .find(|set| set.sticky_layer.mapped().any(|m| m == mapped)) - { - let geometry = set - .sticky_layer - .element_geometry(mapped) - .unwrap() - .to_global(&set.output); - (&mut set.sticky_layer, geometry) - } else if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { - let geometry = workspace - .element_geometry(&mapped) - .unwrap() - .to_global(workspace.output()); - (&mut workspace.floating_layer, geometry) - } else { - return; - }; + let mut start_data = check_grab_preconditions(&seat, &surface, None, ReleaseMode::Click)?; - let new_loc = if edge.contains(ResizeEdge::LEFT) { - Point::::from((geometry.loc.x, geometry.loc.y + (geometry.size.h / 2))) - } else if edge.contains(ResizeEdge::RIGHT) { - Point::::from(( - geometry.loc.x + geometry.size.w, - geometry.loc.y + (geometry.size.h / 2), - )) - } else if edge.contains(ResizeEdge::TOP) { - Point::::from((geometry.loc.x + (geometry.size.w / 2), geometry.loc.y)) - } else if edge.contains(ResizeEdge::BOTTOM) { - Point::::from(( - geometry.loc.x + (geometry.size.w / 2), - geometry.loc.y + geometry.size.h, - )) - } else { - return; - }; - - let element_offset = (new_loc - geometry.loc).as_logical(); - let focus = mapped - .focus_under(element_offset.to_f64()) - .map(|(target, surface_offset)| (target, (surface_offset + element_offset))); - start_data.set_location(new_loc.as_logical().to_f64()); - start_data.set_focus(focus.clone()); + let (floating_layer, geometry) = if let Some(set) = self + .workspaces + .sets + .values_mut() + .find(|set| set.sticky_layer.mapped().any(|m| m == mapped)) + { + let geometry = set + .sticky_layer + .element_geometry(mapped) + .unwrap() + .to_global(&set.output); + (&mut set.sticky_layer, geometry) + } else if let Some(workspace) = self.space_for_mut(&mapped) { + let geometry = workspace + .element_geometry(&mapped) + .unwrap() + .to_global(workspace.output()); + (&mut workspace.floating_layer, geometry) + } else { + return None; + }; - let is_touch_grab = matches!(start_data, GrabStartData::Touch(_)); + let new_loc = if edge.contains(ResizeEdge::LEFT) { + Point::::from((geometry.loc.x, geometry.loc.y + (geometry.size.h / 2))) + } else if edge.contains(ResizeEdge::RIGHT) { + Point::::from(( + geometry.loc.x + geometry.size.w, + geometry.loc.y + (geometry.size.h / 2), + )) + } else if edge.contains(ResizeEdge::TOP) { + Point::::from((geometry.loc.x + (geometry.size.w / 2), geometry.loc.y)) + } else if edge.contains(ResizeEdge::BOTTOM) { + Point::::from(( + geometry.loc.x + (geometry.size.w / 2), + geometry.loc.y + geometry.size.h, + )) + } else { + return None; + }; - let grab: ResizeGrab = if let Some(grab) = floating_layer.resize_request( - mapped, - seat, - start_data.clone(), - edge, + let element_offset = (new_loc - geometry.loc).as_logical(); + let focus = mapped + .focus_under(element_offset.to_f64()) + .map(|(target, surface_offset)| (target, (surface_offset + element_offset))); + start_data.set_location(new_loc.as_logical().to_f64()); + start_data.set_focus(focus.clone()); + + let grab: ResizeGrab = if let Some(grab) = floating_layer.resize_request( + mapped, + seat, + start_data.clone(), + edge, + ReleaseMode::Click, + ) { + grab.into() + } else if let Some(ws) = self.space_for_mut(&mapped) { + let node_id = mapped.tiling_node_id.lock().unwrap().clone()?; + let (node, left_up_idx, orientation) = ws.tiling_layer.resize_request(node_id, edge)?; + ResizeForkGrab::new( + start_data, + new_loc.to_f64(), + node, + left_up_idx, + orientation, + ws.output.downgrade(), ReleaseMode::Click, - ) { - grab.into() - } else if let Some(ws) = state.common.shell.space_for_mut(&mapped) { - let Some(node_id) = mapped.tiling_node_id.lock().unwrap().clone() else { - return; - }; - let Some((node, left_up_idx, orientation)) = - ws.tiling_layer.resize_request(node_id, edge) - else { - return; - }; - ResizeForkGrab::new( - start_data, - new_loc.to_f64(), - node, - left_up_idx, - orientation, - ws.output.downgrade(), - ReleaseMode::Click, - ) - .into() - } else { - return; - }; + ) + .into() + } else { + return None; + }; - let serial = SERIAL_COUNTER.next_serial(); - if is_touch_grab { - seat.get_touch().unwrap().set_grab(state, grab, serial); - } else { - let ptr = seat.get_pointer().unwrap(); - ptr.motion( - state, - focus, - &MotionEvent { - location: new_loc.as_logical().to_f64(), - serial, - time: 0, - }, - ); - ptr.frame(state); - ptr.set_grab(state, grab, serial, Focus::Keep); - } - } + Some(((focus, new_loc), (grab, Focus::Keep))) } pub fn maximize_toggle(&mut self, window: &CosmicMapped, seat: &Seat) { @@ -2897,9 +2749,7 @@ impl Shell { .values_mut() .find(|set| set.sticky_layer.mapped().any(|m| m == mapped)) { - let to = self - .toplevel_management_state - .minimize_rectangle(&set.output, &mapped.active_window()); + let to = minimize_rectangle(&set.output, &mapped.active_window()); let (window, position) = set.sticky_layer.unmap_minimize(mapped, to).unwrap(); set.minimized_windows.push(MinimizedWindow { window, @@ -2912,9 +2762,7 @@ impl Shell { .iter_mut() .find(|workspace| workspace.mapped().any(|m| m == mapped)) }) { - let to = self - .toplevel_management_state - .minimize_rectangle(workspace.output(), &mapped.active_window()); + let to = minimize_rectangle(workspace.output(), &mapped.active_window()); if let Some(minimized) = workspace.minimize(&mapped, to) { workspace.minimized_windows.push(minimized); } @@ -2929,9 +2777,7 @@ impl Shell { .map(|i| set.minimized_windows.swap_remove(i)) .map(|window| (set, window)) }) { - let from = self - .toplevel_management_state - .minimize_rectangle(&set.output, &mapped.active_window()); + let from = minimize_rectangle(&set.output, &mapped.active_window()); if let MinimizedState::Sticky { mut position } = window.previous_state { let current_output_size = set.output.geometry().size.as_logical(); @@ -2957,9 +2803,7 @@ impl Shell { .map(|i| w.minimized_windows.swap_remove(i)) .map(|window| (w, window)) }) { - let from = self - .toplevel_management_state - .minimize_rectangle(workspace.output(), &mapped.active_window()); + let from = minimize_rectangle(workspace.output(), &mapped.active_window()); workspace.unminimize(window, from, seat); } @@ -3034,85 +2878,60 @@ impl Shell { } pub fn resize_request( - state: &mut State, + &mut self, surface: &WlSurface, seat: &Seat, serial: impl Into>, edges: ResizeEdge, - ) { + ) -> Option<(ResizeGrab, Focus)> { let serial = serial.into(); - if let Some(start_data) = - check_grab_preconditions(&seat, surface, serial, ReleaseMode::NoMouseButtons) - { - if let Some(mapped) = state.common.shell.element_for_surface(surface).cloned() { - if mapped.is_fullscreen(true) || mapped.is_maximized(true) { - return; - } - - let floating_layer = if let Some(set) = state - .common - .shell - .workspaces - .sets - .values_mut() - .find(|set| set.sticky_layer.mapped().any(|m| m == &mapped)) - { - &mut set.sticky_layer - } else if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { - &mut workspace.floating_layer - } else { - return; - }; + let start_data = + check_grab_preconditions(&seat, surface, serial, ReleaseMode::NoMouseButtons)?; + let mapped = self.element_for_surface(surface).cloned()?; + if mapped.is_fullscreen(true) || mapped.is_maximized(true) { + return None; + } - let is_touch_grab = matches!(start_data, GrabStartData::Touch(_)); + let floating_layer = if let Some(set) = self + .workspaces + .sets + .values_mut() + .find(|set| set.sticky_layer.mapped().any(|m| m == &mapped)) + { + &mut set.sticky_layer + } else if let Some(workspace) = self.space_for_mut(&mapped) { + &mut workspace.floating_layer + } else { + return None; + }; - let grab: ResizeGrab = if let Some(grab) = floating_layer.resize_request( - &mapped, - seat, - start_data.clone(), - edges, - ReleaseMode::NoMouseButtons, - ) { - grab.into() - } else if let Some(ws) = state.common.shell.space_for_mut(&mapped) { - let Some(node_id) = mapped.tiling_node_id.lock().unwrap().clone() else { - return; - }; - let Some((node, left_up_idx, orientation)) = - ws.tiling_layer.resize_request(node_id, edges) - else { - return; - }; - ResizeForkGrab::new( - start_data, - seat.get_pointer().unwrap().current_location().as_global(), - node, - left_up_idx, - orientation, - ws.output.downgrade(), - ReleaseMode::NoMouseButtons, - ) - .into() - } else { - return; - }; + let grab: ResizeGrab = if let Some(grab) = floating_layer.resize_request( + &mapped, + seat, + start_data.clone(), + edges, + ReleaseMode::NoMouseButtons, + ) { + grab.into() + } else if let Some(ws) = self.space_for_mut(&mapped) { + let node_id = mapped.tiling_node_id.lock().unwrap().clone()?; + let (node, left_up_idx, orientation) = + ws.tiling_layer.resize_request(node_id, edges)?; + ResizeForkGrab::new( + start_data, + seat.get_pointer().unwrap().current_location().as_global(), + node, + left_up_idx, + orientation, + ws.output.downgrade(), + ReleaseMode::NoMouseButtons, + ) + .into() + } else { + return None; + }; - if is_touch_grab { - seat.get_touch().unwrap().set_grab( - state, - grab, - serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()), - ); - } else { - seat.get_pointer().unwrap().set_grab( - state, - grab, - serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()), - Focus::Clear, - ); - } - } - } + Some((grab, Focus::Clear)) } pub fn resize(&mut self, seat: &Seat, direction: ResizeDirection, edge: ResizeEdge) { @@ -3260,8 +3079,7 @@ impl Shell { let handle = workspace.handle; for (window, _) in mapped.windows() { - self.toplevel_info_state - .toplevel_leave_workspace(&window, &handle); + toplevel_leave_workspace(&window, &handle); } self.workspaces @@ -3281,8 +3099,7 @@ impl Shell { let workspace = &mut set.workspaces[set.active]; for (window, _) in mapped.windows() { - self.toplevel_info_state - .toplevel_enter_workspace(&window, &workspace.handle); + toplevel_enter_workspace(&window, &workspace.handle); } match mapped @@ -3315,28 +3132,16 @@ impl Shell { } } - pub(crate) fn set_theme(&mut self, theme: cosmic::Theme) { + pub fn set_theme( + &mut self, + theme: cosmic::Theme, + xdg_activation_state: &XdgActivationState, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, + ) { self.theme = theme.clone(); - self.refresh(); - self.workspaces - .set_theme(theme.clone(), &self.xdg_activation_state); - } - - pub fn set_urgent(&mut self, workspace: &WorkspaceHandle) { - 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.refresh(xdg_activation_state, workspace_state); self.workspaces - .update_autotile_behavior(behavior, &mut guard, self.seats.iter()) + .set_theme(theme.clone(), xdg_activation_state); } } diff --git a/src/shell/seats.rs b/src/shell/seats.rs index 1c040959..f05cf457 100644 --- a/src/shell/seats.rs +++ b/src/shell/seats.rs @@ -206,6 +206,8 @@ pub trait SeatExt { fn active_output(&self) -> Output; fn set_active_output(&self, output: &Output); fn devices(&self) -> &Devices; + fn supressed_keys(&self) -> &SupressedKeys; + fn modifiers_shortcut_queue(&self) -> &ModifiersShortcutQueue; fn cursor_geometry( &self, @@ -239,6 +241,14 @@ impl SeatExt for Seat { self.user_data().get::().unwrap() } + fn supressed_keys(&self) -> &SupressedKeys { + self.user_data().get::().unwrap() + } + + fn modifiers_shortcut_queue(&self) -> &ModifiersShortcutQueue { + self.user_data().get::().unwrap() + } + fn cursor_geometry( &self, loc: impl Into>, diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 6ffffcec..3d575cc3 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -12,7 +12,7 @@ use crate::{ wayland::{ handlers::screencopy::ScreencopySessions, protocols::{ - toplevel_info::ToplevelInfoState, + toplevel_info::{toplevel_enter_output, toplevel_leave_output}, workspace::{WorkspaceHandle, WorkspaceUpdateGuard}, }, }, @@ -365,23 +365,19 @@ impl Workspace { &self.output } - pub fn set_output( - &mut self, - output: &Output, - toplevel_info: &mut ToplevelInfoState, - ) { + pub fn set_output(&mut self, output: &Output) { self.tiling_layer.set_output(output); self.floating_layer.set_output(output); for mapped in self.mapped() { for (surface, _) in mapped.windows() { - toplevel_info.toplevel_leave_output(&surface, &self.output); - toplevel_info.toplevel_enter_output(&surface, output); + toplevel_leave_output(&surface, &self.output); + toplevel_enter_output(&surface, output); } } for window in self.minimized_windows.iter() { for (surface, _) in window.window.windows() { - toplevel_info.toplevel_leave_output(&surface, &self.output); - toplevel_info.toplevel_enter_output(&surface, output); + toplevel_leave_output(&surface, &self.output); + toplevel_enter_output(&surface, output); } } let output_name = output.name(); diff --git a/src/state.rs b/src/state.rs index 0df36ae0..8ed9a22f 100644 --- a/src/state.rs +++ b/src/state.rs @@ -4,12 +4,17 @@ use crate::{ backend::{kms::KmsState, winit::WinitState, x11::X11State}, config::{Config, OutputConfig}, input::gestures::GestureState, - shell::{grabs::SeatMoveGrabState, SeatExt, Shell}, + shell::{grabs::SeatMoveGrabState, CosmicSurface, SeatExt, Shell}, wayland::protocols::{ - drm::WlDrmState, image_source::ImageSourceState, - output_configuration::OutputConfigurationState, screencopy::ScreencopyState, - workspace::WorkspaceClientState, + drm::WlDrmState, + image_source::ImageSourceState, + output_configuration::OutputConfigurationState, + screencopy::ScreencopyState, + toplevel_info::ToplevelInfoState, + toplevel_management::{ManagementCapabilities, ToplevelManagementState}, + workspace::{WorkspaceClientState, WorkspaceState, WorkspaceUpdateGuard}, }, + xwayland::XWaylandState, }; use anyhow::Context; use i18n_embed::{ @@ -41,11 +46,13 @@ use smithay::{ take_presentation_feedback_surface_tree, update_surface_primary_scanout_output, with_surfaces_surface_tree, OutputPresentationFeedback, }, + PopupManager, }, input::{pointer::CursorImageStatus, SeatState}, output::{Mode as OutputMode, Output, Scale}, reexports::{ calloop::{LoopHandle, LoopSignal}, + wayland_protocols::xdg::shell::server::xdg_toplevel::WmCapabilities, wayland_protocols_misc::server_decoration::server::org_kde_kwin_server_decoration_manager::Mode, wayland_server::{ backend::{ClientData, ClientId, DisconnectReason}, @@ -71,12 +78,17 @@ use smithay::{ wlr_data_control::DataControlState, }, session_lock::SessionLockManagerState, - shell::{kde::decoration::KdeDecorationState, xdg::decoration::XdgDecorationState}, + shell::{ + kde::decoration::KdeDecorationState, + wlr_layer::WlrLayerShellState, + xdg::{decoration::XdgDecorationState, XdgShellState}, + }, shm::ShmState, tablet_manager::TabletManagerState, text_input::TextInputManagerState, viewporter::ViewporterState, virtual_keyboard::VirtualKeyboardManagerState, + xdg_activation::XdgActivationState, xwayland_keyboard_grab::XWaylandKeyboardGrabState, }, xwayland::XWaylandClientData, @@ -84,6 +96,7 @@ use smithay::{ use time::UtcOffset; use tracing::error; +use std::sync::{Arc, RwLock}; use std::{cell::RefCell, ffi::OsString, time::Duration}; use std::{collections::VecDeque, time::Instant}; @@ -147,7 +160,8 @@ pub struct Common { pub event_loop_handle: LoopHandle<'static, State>, pub event_loop_signal: LoopSignal, - pub shell: Shell, + pub popups: PopupManager, + pub shell: Arc>, pub clock: Clock, pub should_stop: bool, @@ -179,6 +193,15 @@ pub struct Common { pub viewporter_state: ViewporterState, pub kde_decoration_state: KdeDecorationState, pub xdg_decoration_state: XdgDecorationState, + + // shell-related wayland state + pub xdg_shell_state: XdgShellState, + pub layer_shell_state: WlrLayerShellState, + pub toplevel_info_state: ToplevelInfoState, + pub toplevel_management_state: ToplevelManagementState, + pub xdg_activation_state: XdgActivationState, + pub workspace_state: WorkspaceState, + pub xwayland_state: Option, } #[derive(Debug)] @@ -225,11 +248,18 @@ impl BackendData { test_only: bool, shell: &mut Shell, loop_handle: &LoopHandle<'_, State>, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, + xdg_activation_state: &XdgActivationState, ) -> Result<(), anyhow::Error> { let result = match self { - BackendData::Kms(ref mut state) => { - state.apply_config_for_output(output, shell, test_only, loop_handle) - } + BackendData::Kms(ref mut state) => state.apply_config_for_output( + output, + shell, + test_only, + loop_handle, + workspace_state, + xdg_activation_state, + ), 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), _ => unreachable!("No backend set when applying output config"), @@ -389,7 +419,36 @@ impl State { DataControlState::new::(dh, Some(&primary_selection_state), |_| true) }); - let shell = Shell::new(&config, dh); + let shell = Arc::new(RwLock::new(Shell::new(&config))); + + let layer_shell_state = WlrLayerShellState::new_with_filter::( + dh, + client_should_see_privileged_protocols, + ); + let xdg_shell_state = XdgShellState::new_with_capabilities::( + dh, + [ + WmCapabilities::Fullscreen, + WmCapabilities::Maximize, + WmCapabilities::Minimize, + WmCapabilities::WindowMenu, + ], + ); + let xdg_activation_state = XdgActivationState::new::(dh); + let toplevel_info_state = + ToplevelInfoState::new(dh, client_should_see_privileged_protocols); + let toplevel_management_state = ToplevelManagementState::new::( + dh, + vec![ + ManagementCapabilities::Close, + ManagementCapabilities::Activate, + ManagementCapabilities::Maximize, + ManagementCapabilities::Minimize, + ManagementCapabilities::MoveToWorkspace, + ], + client_should_see_privileged_protocols, + ); + let workspace_state = WorkspaceState::new(dh, client_should_see_privileged_protocols); if let Err(err) = crate::dbus::init(&handle) { tracing::warn!(?err, "Failed to initialize dbus handlers"); @@ -403,6 +462,7 @@ impl State { event_loop_handle: handle, event_loop_signal: signal, + popups: PopupManager::default(), shell, local_offset, @@ -441,6 +501,13 @@ impl State { wl_drm_state, kde_decoration_state, xdg_decoration_state, + xdg_shell_state, + layer_shell_state, + toplevel_info_state, + toplevel_management_state, + xdg_activation_state, + workspace_state, + xwayland_state: None, }, backend: BackendData::Unset, } @@ -495,8 +562,9 @@ impl Common { ) { let time = self.clock.now(); let throttle = Some(Duration::from_secs(1)); + let shell = self.shell.read().unwrap(); - if let Some(session_lock) = self.shell.session_lock.as_ref() { + if let Some(session_lock) = shell.session_lock.as_ref() { if let Some(lock_surface) = session_lock.surfaces.get(output) { with_surfaces_surface_tree(lock_surface.wl_surface(), |_surface, states| { with_fractional_scale(states, |fraction_scale| { @@ -532,8 +600,7 @@ impl Common { } } - for seat in self - .shell + for seat in shell .seats .iter() .filter(|seat| &seat.active_output() == output) @@ -595,7 +662,7 @@ impl Common { } } - self.shell + shell .workspaces .sets .get(output) @@ -636,7 +703,7 @@ impl Common { } }); - let active = self.shell.active_space(output); + let active = shell.active_space(output); active.mapped().for_each(|mapped| { let window = mapped.active_window(); window.with_surfaces(|surface, states| { @@ -675,8 +742,7 @@ impl Common { window.send_frame(output, time, throttle, |_, _| None); }); - for space in self - .shell + for space in shell .workspaces .spaces_for_output(output) .filter(|w| w.handle != active.handle) @@ -691,7 +757,7 @@ impl Common { }) } - self.shell.override_redirect_windows.iter().for_each(|or| { + shell.override_redirect_windows.iter().for_each(|or| { if let Some(wl_surface) = or.wl_surface() { with_surfaces_surface_tree(&wl_surface, |surface, states| { let primary_scanout_output = update_surface_primary_scanout_output( @@ -780,8 +846,9 @@ impl Common { render_element_states: &RenderElementStates, ) -> OutputPresentationFeedback { let mut output_presentation_feedback = OutputPresentationFeedback::new(output); + let shell = self.shell.read().unwrap(); - let active = self.shell.active_space(output); + let active = shell.active_space(output); active.mapped().for_each(|mapped| { mapped.active_window().take_presentation_feedback( &mut output_presentation_feedback, @@ -792,7 +859,7 @@ impl Common { ); }); - self.shell.override_redirect_windows.iter().for_each(|or| { + shell.override_redirect_windows.iter().for_each(|or| { if let Some(wl_surface) = or.wl_surface() { take_presentation_feedback_surface_tree( &wl_surface, diff --git a/src/systemd.rs b/src/systemd.rs index 244e419b..d75f3f82 100644 --- a/src/systemd.rs +++ b/src/systemd.rs @@ -14,7 +14,6 @@ pub fn ready(state: &State) { "DISPLAY", &state .common - .shell .xwayland_state .as_ref() .map(|s| format!(":{}", s.display)) diff --git a/src/theme.rs b/src/theme.rs index 89c4e3b4..d2f3c005 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -32,13 +32,12 @@ pub fn watch_theme(handle: LoopHandle<'_, State>) -> Result<(), cosmic_config::E if theme.theme_type != new_theme.theme_type { *theme = new_theme; - state.common.shell.set_theme(theme.clone()); - state.common.shell.workspaces.spaces().for_each(|s| { - s.mapped().for_each(|m| { - m.update_theme(theme.clone()); - m.force_redraw(); - }) - }); + let mut workspace_guard = state.common.workspace_state.update(); + state.common.shell.write().unwrap().set_theme( + theme.clone(), + &state.common.xdg_activation_state, + &mut workspace_guard, + ); } }) { tracing::error!("{e}"); diff --git a/src/wayland/handlers/compositor.rs b/src/wayland/handlers/compositor.rs index cb36a002..f588dec8 100644 --- a/src/wayland/handlers/compositor.rs +++ b/src/wayland/handlers/compositor.rs @@ -26,68 +26,59 @@ use smithay::{ }; use std::sync::Mutex; -impl State { - fn toplevel_ensure_initial_configure(&mut self, toplevel: &ToplevelSurface) -> bool { - // send the initial configure if relevant - let initial_configure_sent = with_states(toplevel.wl_surface(), |states| { - states - .data_map - .get::>() - .unwrap() - .lock() - .unwrap() - .initial_configure_sent - }); - if !initial_configure_sent { - // TODO: query expected size from shell (without inserting and mapping) - toplevel.with_pending_state(|states| states.size = None); - toplevel.send_configure(); - } - initial_configure_sent - } - - fn xdg_popup_ensure_initial_configure(&mut self, popup: &PopupKind) { - if let PopupKind::Xdg(ref popup) = popup { - let initial_configure_sent = with_states(popup.wl_surface(), |states| { - states - .data_map - .get::>() - .unwrap() - .lock() - .unwrap() - .initial_configure_sent - }); - if !initial_configure_sent { - // NOTE: This should never fail as the initial configure is always - // allowed. - popup.send_configure().expect("initial configure failed"); - } - } +fn toplevel_ensure_initial_configure(toplevel: &ToplevelSurface) -> bool { + // send the initial configure if relevant + let initial_configure_sent = with_states(toplevel.wl_surface(), |states| { + states + .data_map + .get::>() + .unwrap() + .lock() + .unwrap() + .initial_configure_sent + }); + if !initial_configure_sent { + // TODO: query expected size from shell (without inserting and mapping) + toplevel.with_pending_state(|states| states.size = None); + toplevel.send_configure(); } + initial_configure_sent +} - fn layer_surface_ensure_inital_configure(&mut self, surface: &LayerSurface) -> bool { - // send the initial configure if relevant - let initial_configure_sent = with_states(surface.wl_surface(), |states| { +fn xdg_popup_ensure_initial_configure(popup: &PopupKind) { + if let PopupKind::Xdg(ref popup) = popup { + let initial_configure_sent = with_states(popup.wl_surface(), |states| { states .data_map - .get::>() + .get::>() .unwrap() .lock() .unwrap() .initial_configure_sent }); if !initial_configure_sent { - // compute initial dimensions by mapping - 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(); + // NOTE: This should never fail as the initial configure is always + // allowed. + popup.send_configure().expect("initial configure failed"); } - initial_configure_sent } } +fn layer_surface_check_inital_configure(surface: &LayerSurface) -> bool { + // send the initial configure if relevant + let initial_configure_sent = with_states(surface.wl_surface(), |states| { + states + .data_map + .get::>() + .unwrap() + .lock() + .unwrap() + .initial_configure_sent + }); + + initial_configure_sent +} + pub fn client_compositor_state<'a>(client: &'a Client) -> &'a CompositorClientState { if let Some(state) = client.get_data::() { return &state.compositor_state; @@ -147,48 +138,67 @@ impl CompositorHandler for State { // first load the buffer for various smithay helper functions (which also initializes the RendererSurfaceState) on_commit_buffer_handler::(surface); + // and refresh smithays internal state + self.common.on_commit(surface); + + let mut shell = self.common.shell.write().unwrap(); + + // schedule a new render + if let Some(output) = shell.visible_output_for_surface(surface) { + self.backend + .schedule_render(&self.common.event_loop_handle, &output); + } + // handle initial configure events and map windows if necessary - if let Some((window, _, _)) = self - .common - .shell + if let Some((window, _, _)) = shell .pending_windows .iter() .find(|(window, _, _)| window.wl_surface().as_ref() == Some(surface)) .cloned() { if let Some(toplevel) = window.0.toplevel() { - if self.toplevel_ensure_initial_configure(&toplevel) + if toplevel_ensure_initial_configure(&toplevel) && with_renderer_surface_state(&surface, |state| state.buffer().is_some()) .unwrap_or(false) { window.on_commit(); - if let Some(target) = self.common.shell.map_window( + let res = shell.map_window( &window, + &mut self.common.toplevel_info_state, + &mut self.common.workspace_state, &self.common.event_loop_handle, - &self.common.theme, - ) { - let seat = self.common.shell.seats.last_active().clone(); + ); + if let Some(target) = res { + let seat = shell.seats.last_active().clone(); + std::mem::drop(shell); Shell::set_focus(self, Some(&target), &seat, None); + return; } } } } - if let Some((layer_surface, _, _)) = self - .common - .shell + if let Some((layer_surface, _, _)) = shell .pending_layers .iter() .find(|(layer_surface, _, _)| layer_surface.wl_surface() == surface) .cloned() { - if !self.layer_surface_ensure_inital_configure(&layer_surface) { + if !layer_surface_check_inital_configure(&layer_surface) { + // compute initial dimensions by mapping + if let Some(target) = shell.map_layer(&layer_surface) { + let seat = shell.seats.last_active().clone(); + std::mem::drop(shell); + Shell::set_focus(self, Some(&target), &seat, None); + } + layer_surface.layer_surface().send_configure(); return; } }; - if let Some(popup) = self.common.shell.popups.find_popup(surface) { - self.xdg_popup_ensure_initial_configure(&popup); + if let Some(popup) = self.common.popups.find_popup(surface) { + xdg_popup_ensure_initial_configure(&popup); + return; } if with_renderer_surface_state(surface, |state| state.buffer().is_none()).unwrap_or(false) { @@ -197,7 +207,7 @@ impl CompositorHandler for State { // session-lock disallows null commits // if it was a move-grab? - let seat = self.common.shell.seats.last_active().clone(); + let seat = shell.seats.last_active().clone(); let moved_window = seat .user_data() .get::() @@ -228,9 +238,11 @@ impl CompositorHandler for State { stack.remove_idx(i); } } else { + std::mem::drop(shell); seat.get_pointer() .unwrap() .unset_grab(self, SERIAL_COUNTER.next_serial(), 0); + return; } } @@ -247,21 +259,16 @@ impl CompositorHandler for State { // If we would re-position the window inside the grab we would get a weird jittery animation. // We only want to resize once the client has acknoledged & commited the new size, // so we need to carefully track the state through different handlers. - if let Some(element) = self.common.shell.element_for_surface(surface).cloned() { + if let Some(element) = shell.element_for_surface(surface).cloned() { crate::shell::layout::floating::ResizeSurfaceGrab::apply_resize_to_location( element.clone(), - &mut self.common.shell, + &mut *shell, ); } } - // and refresh smithays internal state - self.common.shell.on_commit(surface); - // re-arrange layer-surfaces (commits may change size and positioning) - let layer_output = self - .common - .shell + let layer_output = shell .outputs() .find(|o| { let map = layer_map_for_output(o); @@ -269,18 +276,13 @@ impl CompositorHandler for State { .is_some() }) .cloned(); + if let Some(output) = layer_output { let changed = layer_map_for_output(&output).arrange(); if changed { - self.common.shell.workspaces.recalculate(); + shell.workspaces.recalculate(); } } - - // schedule a new render - if let Some(output) = self.common.shell.visible_output_for_surface(surface) { - self.backend - .schedule_render(&self.common.event_loop_handle, &output); - } } } diff --git a/src/wayland/handlers/decoration.rs b/src/wayland/handlers/decoration.rs index f1fd6ac3..4ca004ba 100644 --- a/src/wayland/handlers/decoration.rs +++ b/src/wayland/handlers/decoration.rs @@ -54,86 +54,85 @@ impl PreferredDecorationMode { } } -impl State { - pub fn new_decoration(mapped: &CosmicMapped, surface: &WlSurface) -> KdeMode { - if mapped.is_stack() { - if let Some((window, _)) = mapped - .windows() - .find(|(window, _)| window.wl_surface().as_ref() == Some(surface)) - { - if let Some(toplevel) = window.0.toplevel() { - toplevel.with_pending_state(|state| { - state.decoration_mode = Some(XdgMode::ServerSide) - }); - toplevel.send_configure(); - } - } - KdeMode::Server - } else { - if let Some((window, _)) = mapped - .windows() - .find(|(window, _)| window.wl_surface().as_ref() == Some(surface)) - { - if let Some(toplevel) = window.0.toplevel() { - toplevel.with_pending_state(|state| { - state.decoration_mode = Some(XdgMode::ClientSide) - }); - toplevel.send_configure(); - } - } - KdeMode::Client - } - } - - pub fn request_mode(mapped: &CosmicMapped, surface: &WlSurface, mode: XdgMode) { +pub fn new_decoration(mapped: &CosmicMapped, surface: &WlSurface) -> KdeMode { + if mapped.is_stack() { if let Some((window, _)) = mapped .windows() .find(|(window, _)| window.wl_surface().as_ref() == Some(surface)) { if let Some(toplevel) = window.0.toplevel() { - PreferredDecorationMode::update(&window.0, Some(mode)); - toplevel.with_pending_state(|state| { - state.decoration_mode = Some(mode); - }); + toplevel + .with_pending_state(|state| state.decoration_mode = Some(XdgMode::ServerSide)); toplevel.send_configure(); } } - } - - pub fn unset_mode(mapped: &CosmicMapped, surface: &WlSurface) { + KdeMode::Server + } else { if let Some((window, _)) = mapped .windows() .find(|(window, _)| window.wl_surface().as_ref() == Some(surface)) { if let Some(toplevel) = window.0.toplevel() { - PreferredDecorationMode::update(&window.0, None); - toplevel.with_pending_state(|state| { - state.decoration_mode = None; - }); + toplevel + .with_pending_state(|state| state.decoration_mode = Some(XdgMode::ClientSide)); toplevel.send_configure(); } } + KdeMode::Client + } +} + +pub fn request_mode(mapped: &CosmicMapped, surface: &WlSurface, mode: XdgMode) { + if let Some((window, _)) = mapped + .windows() + .find(|(window, _)| window.wl_surface().as_ref() == Some(surface)) + { + if let Some(toplevel) = window.0.toplevel() { + PreferredDecorationMode::update(&window.0, Some(mode)); + toplevel.with_pending_state(|state| { + state.decoration_mode = Some(mode); + }); + toplevel.send_configure(); + } + } +} + +pub fn unset_mode(mapped: &CosmicMapped, surface: &WlSurface) { + if let Some((window, _)) = mapped + .windows() + .find(|(window, _)| window.wl_surface().as_ref() == Some(surface)) + { + if let Some(toplevel) = window.0.toplevel() { + PreferredDecorationMode::update(&window.0, None); + toplevel.with_pending_state(|state| { + state.decoration_mode = None; + }); + toplevel.send_configure(); + } } } impl XdgDecorationHandler for State { fn new_decoration(&mut self, toplevel: ToplevelSurface) { - if let Some(mapped) = self.common.shell.element_for_surface(toplevel.wl_surface()) { - State::new_decoration(mapped, toplevel.wl_surface()); + let shell = self.common.shell.read().unwrap(); + if let Some(mapped) = shell.element_for_surface(toplevel.wl_surface()) { + new_decoration(mapped, toplevel.wl_surface()); } } fn request_mode(&mut self, toplevel: ToplevelSurface, mode: XdgMode) { - if let Some(mapped) = self.common.shell.element_for_surface(toplevel.wl_surface()) { - State::request_mode(mapped, toplevel.wl_surface(), mode); + let shell = self.common.shell.read().unwrap(); + if let Some(mapped) = shell.element_for_surface(toplevel.wl_surface()) { + request_mode(mapped, toplevel.wl_surface(), mode); } else { toplevel.with_pending_state(|state| state.decoration_mode = Some(mode)); } } fn unset_mode(&mut self, toplevel: ToplevelSurface) { - if let Some(mapped) = self.common.shell.element_for_surface(toplevel.wl_surface()) { - State::unset_mode(mapped, toplevel.wl_surface()) + let shell = self.common.shell.read().unwrap(); + if let Some(mapped) = shell.element_for_surface(toplevel.wl_surface()) { + unset_mode(mapped, toplevel.wl_surface()) } } } @@ -144,8 +143,9 @@ impl KdeDecorationHandler for State { } fn new_decoration(&mut self, surface: &WlSurface, decoration: &OrgKdeKwinServerDecoration) { - if let Some(mapped) = self.common.shell.element_for_surface(surface) { - let mode = State::new_decoration(mapped, surface); + let shell = self.common.shell.read().unwrap(); + if let Some(mapped) = shell.element_for_surface(surface) { + let mode = new_decoration(mapped, surface); decoration.mode(mode); } } @@ -157,9 +157,10 @@ impl KdeDecorationHandler for State { mode: WEnum, ) { if let WEnum::Value(mode) = mode { + let shell = self.common.shell.read().unwrap(); // TODO: We need to store this value until it gets mapped and apply it then, if it is not mapped yet. - if let Some(mapped) = self.common.shell.element_for_surface(surface) { - State::request_mode( + if let Some(mapped) = shell.element_for_surface(surface) { + request_mode( mapped, surface, match mode { @@ -173,8 +174,9 @@ impl KdeDecorationHandler for State { } fn release(&mut self, _decoration: &OrgKdeKwinServerDecoration, surface: &WlSurface) { - if let Some(mapped) = self.common.shell.element_for_surface(surface) { - State::unset_mode(mapped, surface) + let shell = self.common.shell.read().unwrap(); + if let Some(mapped) = shell.element_for_surface(surface) { + unset_mode(mapped, surface) } } } diff --git a/src/wayland/handlers/fractional_scale.rs b/src/wayland/handlers/fractional_scale.rs index e79e5b27..b2cf0a08 100644 --- a/src/wayland/handlers/fractional_scale.rs +++ b/src/wayland/handlers/fractional_scale.rs @@ -41,11 +41,21 @@ impl FractionalScaleHandler for State { .or_else(|| { self.common .shell + .read() + .unwrap() .visible_output_for_surface(&surface) .cloned() }) }) - .unwrap_or_else(|| self.common.shell.seats.last_active().active_output()); + .unwrap_or_else(|| { + self.common + .shell + .read() + .unwrap() + .seats + .last_active() + .active_output() + }); with_states(&surface, |states| { with_fractional_scale(states, |fractional_scale| { diff --git a/src/wayland/handlers/input_method.rs b/src/wayland/handlers/input_method.rs index 28cc82f0..4e8dc582 100644 --- a/src/wayland/handlers/input_method.rs +++ b/src/wayland/handlers/input_method.rs @@ -12,12 +12,7 @@ use tracing::warn; impl InputMethodHandler for State { fn new_popup(&mut self, surface: PopupSurface) { - if let Err(err) = self - .common - .shell - .popups - .track_popup(PopupKind::from(surface)) - { + if let Err(err) = self.common.popups.track_popup(PopupKind::from(surface)) { warn!("Failed to track popup: {}", err); } } @@ -31,6 +26,8 @@ impl InputMethodHandler for State { fn parent_geometry(&self, parent: &WlSurface) -> Rectangle { self.common .shell + .read() + .unwrap() .element_for_surface(parent) .map(|e| e.geometry()) .unwrap_or_default() diff --git a/src/wayland/handlers/layer_shell.rs b/src/wayland/handlers/layer_shell.rs index 49fd2f04..573057ef 100644 --- a/src/wayland/handlers/layer_shell.rs +++ b/src/wayland/handlers/layer_shell.rs @@ -16,7 +16,7 @@ use smithay::{ impl WlrLayerShellHandler for State { fn shell_state(&mut self) -> &mut WlrLayerShellState { - &mut self.common.shell.layer_shell_state + &mut self.common.layer_shell_state } fn new_layer_surface( @@ -26,24 +26,22 @@ impl WlrLayerShellHandler for State { _layer: Layer, namespace: String, ) { - let seat = self.common.shell.seats.last_active().clone(); + let mut shell = self.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); let output = wl_output .as_ref() .and_then(Output::from_resource) .unwrap_or_else(|| seat.active_output()); - self.common.shell.pending_layers.push(( - LayerSurface::new(surface, namespace), - output, - seat, - )); + shell + .pending_layers + .push((LayerSurface::new(surface, namespace), output, seat)); } fn new_popup(&mut self, _parent: WlrLayerSurface, popup: PopupSurface) { - self.common.shell.unconstrain_popup(&popup); + self.common.shell.read().unwrap().unconstrain_popup(&popup); if popup.send_configure().is_ok() { self.common - .shell .popups .track_popup(PopupKind::from(popup)) .unwrap(); @@ -51,9 +49,8 @@ impl WlrLayerShellHandler for State { } fn layer_destroyed(&mut self, surface: WlrLayerSurface) { - let maybe_output = self - .common - .shell + let mut shell = self.common.shell.write().unwrap(); + let maybe_output = shell .outputs() .find(|o| { let map = layer_map_for_output(o); @@ -72,7 +69,7 @@ impl WlrLayerShellHandler for State { map.unmap_layer(&layer); } - self.common.shell.workspaces.recalculate(); + shell.workspaces.recalculate(); self.backend .schedule_render(&self.common.event_loop_handle, &output); diff --git a/src/wayland/handlers/output_configuration.rs b/src/wayland/handlers/output_configuration.rs index 2880be8b..433b49a7 100644 --- a/src/wayland/handlers/output_configuration.rs +++ b/src/wayland/handlers/output_configuration.rs @@ -82,12 +82,16 @@ impl State { } } - if let Err(err) = self.backend.apply_config_for_output( + let mut shell = self.common.shell.write().unwrap(); + let res = self.backend.apply_config_for_output( output, test_only, - &mut self.common.shell, + &mut *shell, &self.common.event_loop_handle, - ) { + &mut self.common.workspace_state.update(), + &self.common.xdg_activation_state, + ); + if let Err(err) = res { warn!( ?err, "Failed to apply config to {}. Resetting", @@ -106,8 +110,10 @@ impl State { if let Err(err) = self.backend.apply_config_for_output( output, false, - &mut self.common.shell, + &mut *shell, &self.common.event_loop_handle, + &mut self.common.workspace_state.update(), + &self.common.xdg_activation_state, ) { error!(?err, "Failed to reset output config for {}.", output.name(),); } @@ -115,6 +121,9 @@ impl State { } return false; } + + std::mem::drop(shell); + self.common.refresh(); } for output in conf diff --git a/src/wayland/handlers/screencopy/mod.rs b/src/wayland/handlers/screencopy/mod.rs index 3e3f0cff..9642f972 100644 --- a/src/wayland/handlers/screencopy/mod.rs +++ b/src/wayland/handlers/screencopy/mod.rs @@ -52,8 +52,9 @@ impl ScreencopyHandler for State { .upgrade() .and_then(|output| constraints_for_output(&output, &mut self.backend)), ImageSourceData::Workspace(handle) => { - let workspace = self.common.shell.workspaces.space_for_handle(&handle)?; - constraints_for_output(workspace.output(), &mut self.backend) + let shell = self.common.shell.read().unwrap(); + let output = shell.workspaces.space_for_handle(&handle)?.output(); + constraints_for_output(output, &mut self.backend) } ImageSourceData::Toplevel(window) => { constraints_for_toplevel(window, &mut self.backend) @@ -65,6 +66,8 @@ impl ScreencopyHandler for State { let size = if let Some((geometry, _)) = self .common .shell + .read() + .unwrap() .seats .last_active() .cursor_geometry((0.0, 0.0), self.common.clock.now()) @@ -98,8 +101,8 @@ impl ScreencopyHandler for State { output.add_session(session); } ImageSourceData::Workspace(handle) => { - let Some(workspace) = self.common.shell.workspaces.space_for_handle_mut(&handle) - else { + let mut shell = self.common.shell.write().unwrap(); + let Some(workspace) = shell.workspaces.space_for_handle_mut(&handle) else { session.stop(); return; }; @@ -127,7 +130,14 @@ impl ScreencopyHandler for State { } fn new_cursor_session(&mut self, session: CursorSession) { let (pointer_loc, pointer_size, hotspot) = { - let seat = self.common.shell.seats.last_active(); + let seat = self + .common + .shell + .read() + .unwrap() + .seats + .last_active() + .clone(); let pointer = seat.get_pointer().unwrap(); let pointer_loc = pointer.current_location().to_i32_round().as_global(); @@ -183,8 +193,8 @@ impl ScreencopyHandler for State { output.add_cursor_session(session); } ImageSourceData::Workspace(handle) => { - let Some(workspace) = self.common.shell.workspaces.space_for_handle_mut(&handle) - else { + let mut shell = self.common.shell.write().unwrap(); + let Some(workspace) = shell.workspaces.space_for_handle_mut(&handle) else { session.stop(); return; }; @@ -215,9 +225,10 @@ impl ScreencopyHandler for State { workspace.add_cursor_session(session); } ImageSourceData::Toplevel(mut toplevel) => { - if let Some(element) = self.common.shell.element_for_surface(&toplevel) { + let shell = self.common.shell.read().unwrap(); + if let Some(element) = shell.element_for_surface(&toplevel) { if element.has_active_window(&toplevel) { - if let Some(workspace) = self.common.shell.space_for(element) { + if let Some(workspace) = shell.space_for(element) { if let Some(geometry) = workspace.element_geometry(element) { let mut surface_geo = element.active_window_geometry().as_local(); surface_geo.loc += geometry.loc; @@ -268,12 +279,20 @@ impl ScreencopyHandler for State { return; } - let seat = self.common.shell.seats.last_active().clone(); + let seat = self + .common + .shell + .read() + .unwrap() + .seats + .last_active() + .clone(); render_cursor_to_buffer(self, &session, frame, &seat); } fn frame_aborted(&mut self, frame: Frame) { - for mut output in self.common.shell.outputs().cloned() { + let shell = self.common.shell.read().unwrap(); + for mut output in shell.outputs().cloned() { output.remove_frame(&frame) } } @@ -286,7 +305,13 @@ impl ScreencopyHandler for State { } } ImageSourceData::Workspace(handle) => { - if let Some(workspace) = self.common.shell.workspaces.space_for_handle_mut(&handle) + if let Some(workspace) = self + .common + .shell + .write() + .unwrap() + .workspaces + .space_for_handle_mut(&handle) { workspace.remove_session(session) } @@ -304,7 +329,13 @@ impl ScreencopyHandler for State { } } ImageSourceData::Workspace(handle) => { - if let Some(workspace) = self.common.shell.workspaces.space_for_handle_mut(&handle) + if let Some(workspace) = self + .common + .shell + .write() + .unwrap() + .workspaces + .space_for_handle_mut(&handle) { workspace.remove_cursor_session(session) } diff --git a/src/wayland/handlers/screencopy/render.rs b/src/wayland/handlers/screencopy/render.rs index cb7ee76e..5f026229 100644 --- a/src/wayland/handlers/screencopy/render.rs +++ b/src/wayland/handlers/screencopy/render.rs @@ -189,18 +189,16 @@ pub fn render_workspace_to_buffer( #[cfg(feature = "debug")] puffin::profile_function!(); - let Some(workspace) = state.common.shell.workspaces.space_for_handle(&handle) else { + let shell = state.common.shell.read().unwrap(); + let Some(workspace) = shell.workspaces.space_for_handle(&handle) else { session.stop(); return; }; let output = workspace.output().clone(); - let idx = state - .common - .shell - .workspaces - .idx_for_handle(&output, &handle) - .unwrap(); + let idx = shell.workspaces.idx_for_handle(&output, &handle).unwrap(); + std::mem::drop(shell); + let mode = output .current_mode() .map(|mode| mode.size.to_logical(1).to_buffer(1, Transform::Normal)); @@ -286,7 +284,7 @@ pub fn render_workspace_to_buffer( dt, age, additional_damage, - &common.shell, + &*common.shell.read().unwrap(), &common.config, &common.theme, common.clock.now(), @@ -314,7 +312,7 @@ pub fn render_workspace_to_buffer( dt, age, additional_damage, - &common.shell, + &*common.shell.read().unwrap(), &common.config, &common.theme, common.clock.now(), @@ -535,21 +533,23 @@ pub fn render_window_to_buffer( .map(Into::>::into), ); - 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| { - p -= mapped.active_window_offset().to_f64(); - if p.x < 0. || p.y < 0. { - None - } else { - Some(p) - } - }) - } else { - None - } - } { + let shell = common.shell.read().unwrap(); + let seat = shell.seats.last_active().clone(); + let location = if let Some(mapped) = shell.element_for_surface(window) { + mapped.cursor_position(&seat).and_then(|mut p| { + p -= mapped.active_window_offset().to_f64(); + if p.x < 0. || p.y < 0. { + None + } else { + Some(p) + } + }) + } else { + None + }; + std::mem::drop(shell); + + if let Some(location) = location { if draw_cursor { elements.extend( cursor::draw_cursor( diff --git a/src/wayland/handlers/selection.rs b/src/wayland/handlers/selection.rs index c3bcdcf8..6d157a5f 100644 --- a/src/wayland/handlers/selection.rs +++ b/src/wayland/handlers/selection.rs @@ -20,7 +20,6 @@ impl SelectionHandler for State { ) { if let Some(xwm) = self .common - .shell .xwayland_state .as_mut() .and_then(|xstate| xstate.xwm.as_mut()) @@ -45,7 +44,6 @@ impl SelectionHandler for State { ) { if let Some(xwm) = self .common - .shell .xwayland_state .as_mut() .and_then(|xstate| xstate.xwm.as_mut()) diff --git a/src/wayland/handlers/session_lock.rs b/src/wayland/handlers/session_lock.rs index e0f4141f..988df031 100644 --- a/src/wayland/handlers/session_lock.rs +++ b/src/wayland/handlers/session_lock.rs @@ -18,8 +18,10 @@ impl SessionLockHandler for State { } fn lock(&mut self, locker: SessionLocker) { + let mut shell = self.common.shell.write().unwrap(); + // Reject lock if sesion lock exists and is still valid - if let Some(session_lock) = self.common.shell.session_lock.as_ref() { + if let Some(session_lock) = shell.session_lock.as_ref() { if self .common .display_handle @@ -32,28 +34,30 @@ impl SessionLockHandler for State { let ext_session_lock = locker.ext_session_lock().clone(); locker.lock(); - self.common.shell.session_lock = Some(SessionLock { + shell.session_lock = Some(SessionLock { ext_session_lock, surfaces: HashMap::new(), }); - for output in self.common.shell.outputs() { + for output in shell.outputs() { self.backend .schedule_render(&self.common.event_loop_handle, &output); } } fn unlock(&mut self) { - self.common.shell.session_lock = None; + let mut shell = self.common.shell.write().unwrap(); + shell.session_lock = None; - for output in self.common.shell.outputs() { + for output in shell.outputs() { self.backend .schedule_render(&self.common.event_loop_handle, &output); } } fn new_surface(&mut self, lock_surface: LockSurface, wl_output: WlOutput) { - if let Some(session_lock) = &mut self.common.shell.session_lock { + let mut shell = self.common.shell.write().unwrap(); + if let Some(session_lock) = &mut shell.session_lock { if let Some(output) = Output::from_resource(&wl_output) { lock_surface.with_pending_state(|states| { let size = output.geometry().size; diff --git a/src/wayland/handlers/toplevel_info.rs b/src/wayland/handlers/toplevel_info.rs index ce5517f0..1002b93d 100644 --- a/src/wayland/handlers/toplevel_info.rs +++ b/src/wayland/handlers/toplevel_info.rs @@ -14,10 +14,10 @@ impl ToplevelInfoHandler for State { type Window = CosmicSurface; fn toplevel_info_state(&self) -> &ToplevelInfoState { - &self.common.shell.toplevel_info_state + &self.common.toplevel_info_state } fn toplevel_info_state_mut(&mut self) -> &mut ToplevelInfoState { - &mut self.common.shell.toplevel_info_state + &mut self.common.toplevel_info_state } } diff --git a/src/wayland/handlers/toplevel_management.rs b/src/wayland/handlers/toplevel_management.rs index b61bbf8a..24fbdebe 100644 --- a/src/wayland/handlers/toplevel_management.rs +++ b/src/wayland/handlers/toplevel_management.rs @@ -15,15 +15,15 @@ use crate::{ wayland::protocols::{ toplevel_info::ToplevelInfoHandler, toplevel_management::{ - delegate_toplevel_management, ManagementWindow, ToplevelManagementHandler, - ToplevelManagementState, + delegate_toplevel_management, toplevel_rectangle_for, ManagementWindow, + ToplevelManagementHandler, ToplevelManagementState, }, }, }; impl ToplevelManagementHandler for State { fn toplevel_management_state(&mut self) -> &mut ToplevelManagementState { - &mut self.common.shell.toplevel_management_state + &mut self.common.toplevel_management_state } fn activate( @@ -33,17 +33,10 @@ impl ToplevelManagementHandler for State { seat: Option>, ) { self.unminimize(dh, window); - for output in self - .common - .shell - .outputs() - .cloned() - .collect::>() - .iter() - { - let maybe = self - .common - .shell + + let mut shell = self.common.shell.write().unwrap(); + for output in shell.outputs().cloned().collect::>().iter() { + let maybe = shell .workspaces .spaces_for_output(output) .enumerate() @@ -53,19 +46,22 @@ impl ToplevelManagementHandler for State { .any(|w| &w == window) }); if let Some((idx, workspace)) = maybe { - let seat = seat.unwrap_or(self.common.shell.seats.last_active().clone()); + let seat = seat.unwrap_or(shell.seats.last_active().clone()); let mapped = workspace .mapped() .find(|m| m.windows().any(|(w, _)| &w == window)) .unwrap() .clone(); - let _ = self.common.shell.activate( + let _ = shell.activate( &output, idx as usize, WorkspaceDelta::new_shortcut(), + &mut self.common.workspace_state.update(), ); // TODO: Move pointer? mapped.focus_window(window); + + std::mem::drop(shell); Shell::set_focus(self, Some(&mapped.clone().into()), &seat, None); return; } @@ -83,16 +79,12 @@ impl ToplevelManagementHandler for State { workspace: ZcosmicWorkspaceHandleV1, _output: Output, ) { - let Some(to_handle) = self - .common - .shell - .workspace_state - .get_workspace_handle(&workspace) - else { + let Some(to_handle) = self.common.workspace_state.get_workspace_handle(&workspace) else { return; }; - let from_workspace = self.common.shell.workspaces.spaces().find(|w| { + let mut shell = self.common.shell.write().unwrap(); + let from_workspace = shell.workspaces.spaces().find(|w| { w.mapped() .flat_map(|m| m.windows().map(|(s, _)| s)) .any(|w| &w == window) @@ -104,15 +96,18 @@ impl ToplevelManagementHandler for State { .unwrap() .clone(); let from_handle = from_workspace.handle; - let seat = self.common.shell.seats.last_active().clone(); - if let Some((target, _)) = self.common.shell.move_window( + let seat = shell.seats.last_active().clone(); + let res = shell.move_window( Some(&seat), &mapped, &from_handle, &to_handle, false, None, - ) { + &mut self.common.workspace_state.update(), + ); + if let Some((target, _)) = res { + std::mem::drop(shell); Shell::set_focus(self, Some(&target), &seat, None); } return; @@ -125,29 +120,19 @@ impl ToplevelManagementHandler for State { window: &::Window, output: Option, ) { - let seat = self.common.shell.seats.last_active().clone(); - if let Some(mapped) = self.common.shell.element_for_surface(window).cloned() { + let mut shell = self.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); + if let Some(mapped) = shell.element_for_surface(window).cloned() { if let Some(output) = output { - let from = self - .common - .shell - .toplevel_management_state - .minimize_rectangle(&output, window); - let workspace = self.common.shell.workspaces.active_mut(&output); + let from = minimize_rectangle(&output, window); + let workspace = shell.workspaces.active_mut(&output); workspace.fullscreen_request(window, None, from, &seat); - } else if let Some((output, handle)) = self - .common - .shell + } else if let Some((output, handle)) = shell .space_for(&mapped) .map(|workspace| (workspace.output.clone(), workspace.handle.clone())) { - let from = self - .common - .shell - .toplevel_management_state - .minimize_rectangle(&output, window); - self.common - .shell + let from = minimize_rectangle(&output, window); + shell .workspaces .space_for_handle_mut(&handle) .unwrap() @@ -161,20 +146,19 @@ impl ToplevelManagementHandler for State { _dh: &DisplayHandle, window: &::Window, ) { - if let Some(mapped) = self.common.shell.element_for_surface(window).cloned() { - if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { + let mut shell = self.common.shell.write().unwrap(); + if let Some(mapped) = shell.element_for_surface(window).cloned() { + if let Some(workspace) = shell.space_for_mut(&mapped) { if let Some((layer, previous_workspace)) = workspace.unfullscreen_request(window) { let old_handle = workspace.handle.clone(); - let new_workspace_handle = self - .common - .shell + let new_workspace_handle = shell .workspaces .space_for_handle(&previous_workspace) .is_some() .then_some(previous_workspace) .unwrap_or(old_handle); // if the workspace doesn't exist anymore, we can still remap on the right layer - self.common.shell.remap_unfullscreened_window( + shell.remap_unfullscreened_window( mapped, &old_handle, &new_workspace_handle, @@ -186,30 +170,34 @@ 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.shell.seats.last_active().clone(); - self.common.shell.maximize_request(&mapped, &seat); + let mut shell = self.common.shell.write().unwrap(); + if let Some(mapped) = shell.element_for_surface(window).cloned() { + let seat = shell.seats.last_active().clone(); + shell.maximize_request(&mapped, &seat); } } fn unmaximize(&mut self, _dh: &DisplayHandle, window: &::Window) { - if let Some(mapped) = self.common.shell.element_for_surface(window).cloned() { - self.common.shell.unmaximize_request(&mapped); + let mut shell = self.common.shell.write().unwrap(); + if let Some(mapped) = shell.element_for_surface(window).cloned() { + shell.unmaximize_request(&mapped); } } fn minimize(&mut self, _dh: &DisplayHandle, window: &::Window) { - if let Some(mapped) = self.common.shell.element_for_surface(window).cloned() { + let mut shell = self.common.shell.write().unwrap(); + if let Some(mapped) = shell.element_for_surface(window).cloned() { if !mapped.is_stack() || &mapped.active_window() == window { - self.common.shell.minimize_request(&mapped); + shell.minimize_request(&mapped); } } } 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.shell.seats.last_active().clone(); - self.common.shell.unminimize_request(&mapped, &seat); + let mut shell = self.common.shell.write().unwrap(); + if let Some(mut mapped) = shell.element_for_surface(window).cloned() { + let seat = shell.seats.last_active().clone(); + shell.unminimize_request(&mapped, &seat); if mapped.is_stack() { mapped.stack_ref_mut().unwrap().set_active(window); } @@ -223,43 +211,29 @@ impl ManagementWindow for CosmicSurface { } } -pub trait ToplevelManagementExt { - fn minimize_rectangle( - &mut self, - output: &Output, - window: &CosmicSurface, - ) -> Rectangle; -} - -impl ToplevelManagementExt for ToplevelManagementState { - fn minimize_rectangle( - &mut self, - output: &Output, - window: &CosmicSurface, - ) -> Rectangle { - self.rectangle_for(window) - .find_map(|(surface, relative)| { - let map = layer_map_for_output(output); - let layer = map.layer_for_surface(&surface, WindowSurfaceType::ALL); - layer.and_then(|s| map.layer_geometry(s)).map(|local| { - Rectangle::from_loc_and_size( - Point::from((local.loc.x + relative.loc.x, local.loc.y + relative.loc.y)), - relative.size, - ) - }) - }) - .unwrap_or_else(|| { - let output_size = output.geometry().size; +pub fn minimize_rectangle(output: &Output, window: &CosmicSurface) -> Rectangle { + toplevel_rectangle_for(window) + .find_map(|(surface, relative)| { + let map = layer_map_for_output(output); + let layer = map.layer_for_surface(&surface, WindowSurfaceType::ALL); + layer.and_then(|s| map.layer_geometry(s)).map(|local| { Rectangle::from_loc_and_size( - Point::from(( - (output_size.w / 2) - 100, - output_size.h - (output_size.h / 3) - 50, - )), - Size::from((200, 100)), + Point::from((local.loc.x + relative.loc.x, local.loc.y + relative.loc.y)), + relative.size, ) }) - .as_local() - } + }) + .unwrap_or_else(|| { + let output_size = output.geometry().size; + Rectangle::from_loc_and_size( + Point::from(( + (output_size.w / 2) - 100, + output_size.h - (output_size.h / 3) - 50, + )), + Size::from((200, 100)), + ) + }) + .as_local() } delegate_toplevel_management!(State); diff --git a/src/wayland/handlers/workspace.rs b/src/wayland/handlers/workspace.rs index eafbef0e..ee231b7e 100644 --- a/src/wayland/handlers/workspace.rs +++ b/src/wayland/handlers/workspace.rs @@ -21,17 +21,18 @@ impl WorkspaceClientHandler for ClientState { impl WorkspaceHandler for State { type Client = ClientState; fn workspace_state(&self) -> &WorkspaceState { - &self.common.shell.workspace_state + &self.common.workspace_state } fn workspace_state_mut(&mut self) -> &mut WorkspaceState { - &mut self.common.shell.workspace_state + &mut self.common.workspace_state } fn commit_requests(&mut self, _dh: &DisplayHandle, requests: Vec) { for request in requests.into_iter() { match request { Request::Activate(handle) => { - let maybe = self.common.shell.workspaces.iter().find_map(|(o, set)| { + let mut shell = self.common.shell.write().unwrap(); + let maybe = shell.workspaces.iter().find_map(|(o, set)| { set.workspaces .iter() .position(|w| w.handle == handle) @@ -39,22 +40,20 @@ impl WorkspaceHandler for State { }); if let Some((output, idx)) = maybe { - let _ = self.common.shell.activate( + let _ = shell.activate( &output, idx, WorkspaceDelta::new_shortcut(), - ); // TODO: move cursor? + &mut self.common.workspace_state.update(), + ); + // TODO: move cursor? } } Request::SetTilingState { workspace, state } => { - let seat = self.common.shell.seats.last_active().clone(); - if let Some(workspace) = self - .common - .shell - .workspaces - .space_for_handle_mut(&workspace) - { - let mut guard = self.common.shell.workspace_state.update(); + let mut shell = self.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); + if let Some(workspace) = shell.workspaces.space_for_handle_mut(&workspace) { + let mut guard = self.common.workspace_state.update(); workspace.set_tiling( match state.into_result() { Ok(TilingState::FloatingOnly) => false, diff --git a/src/wayland/handlers/xdg_activation.rs b/src/wayland/handlers/xdg_activation.rs index 7b7147c9..4ca6430b 100644 --- a/src/wayland/handlers/xdg_activation.rs +++ b/src/wayland/handlers/xdg_activation.rs @@ -1,3 +1,6 @@ +use crate::{shell::ActivationKey, state::ClientState, utils::prelude::*}; +use crate::{state::State, wayland::protocols::workspace::WorkspaceHandle}; +use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::State as WState; use smithay::{ delegate_xdg_activation, input::Seat, @@ -8,9 +11,6 @@ use smithay::{ }; use tracing::debug; -use crate::{shell::ActivationKey, state::ClientState, utils::prelude::*}; -use crate::{state::State, wayland::protocols::workspace::WorkspaceHandle}; - #[derive(Debug, Clone, Copy)] pub enum ActivationContext { UrgentOnly, @@ -19,7 +19,7 @@ pub enum ActivationContext { impl XdgActivationHandler for State { fn activation_state(&mut self) -> &mut XdgActivationState { - &mut self.common.shell.xdg_activation_state + &mut self.common.xdg_activation_state } fn token_created(&mut self, token: XdgActivationToken, data: XdgActivationTokenData) -> bool { @@ -41,7 +41,8 @@ impl XdgActivationHandler for State { { if let Some(seat) = data.serial.and_then(|(_, seat)| Seat::from_resource(&seat)) { let output = seat.active_output(); - let workspace = self.common.shell.active_space_mut(&output); + let mut shell = self.common.shell.write().unwrap(); + let workspace = shell.active_space_mut(&output); workspace.pending_tokens.insert(token.clone()); let handle = workspace.handle; data.user_data @@ -84,7 +85,8 @@ impl XdgActivationHandler for State { if valid { let output = seat.active_output(); - let workspace = self.common.shell.active_space_mut(&output); + let mut shell = self.common.shell.write().unwrap(); + let workspace = shell.active_space_mut(&output); workspace.pending_tokens.insert(token.clone()); let handle = workspace.handle; data.user_data @@ -105,24 +107,24 @@ impl XdgActivationHandler for State { surface: WlSurface, ) { if let Some(context) = token_data.user_data.get::() { - if let Some(element) = self.common.shell.element_for_surface(&surface).cloned() { + let mut shell = self.common.shell.write().unwrap(); + if let Some(element) = shell.element_for_surface(&surface).cloned() { match context { ActivationContext::UrgentOnly => { - if let Some((workspace, _output)) = - self.common.shell.workspace_for_surface(&surface) - { - self.common.shell.set_urgent(&workspace); + if let Some((workspace, _output)) = shell.workspace_for_surface(&surface) { + let mut workspace_guard = self.common.workspace_state.update(); + workspace_guard.add_workspace_state(&workspace, WState::Urgent); } } ActivationContext::Workspace(workspace) => { - let seat = self.common.shell.seats.last_active().clone(); + let seat = shell.seats.last_active().clone(); let current_output = seat.active_output(); if element.is_minimized() { - self.common.shell.unminimize_request(&element, &seat); + shell.unminimize_request(&element, &seat); } - let current_workspace = self.common.shell.active_space_mut(¤t_output); + let current_workspace = shell.active_space_mut(¤t_output); let in_current_workspace = current_workspace .floating_layer @@ -152,21 +154,19 @@ impl XdgActivationHandler for State { if workspace == ¤t_workspace.handle || in_current_workspace { let target = element.into(); + + std::mem::drop(shell); Shell::set_focus(self, Some(&target), &seat, None); - } else if let Some(w) = self - .common - .shell - .space_for(&element) - .map(|w| w.handle.clone()) + } else if let Some(w) = shell.space_for(&element).map(|w| w.handle.clone()) { - self.common.shell.append_focus_stack(&element, &seat); - self.common.shell.set_urgent(&w); + shell.append_focus_stack(&element, &seat); + let mut workspace_guard = self.common.workspace_state.update(); + workspace_guard.add_workspace_state(&w, WState::Urgent); } } } } else { - self.common - .shell + shell .pending_activations .insert(ActivationKey::Wayland(surface), context.clone()); } diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index fa6de433..343c1913 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -3,6 +3,7 @@ use crate::{ shell::{element::CosmicWindow, grabs::ReleaseMode, CosmicMapped, CosmicSurface, ManagedLayer}, utils::prelude::*, + wayland::protocols::toplevel_info::{toplevel_enter_output, toplevel_enter_workspace}, }; use smithay::{ delegate_xdg_shell, @@ -29,7 +30,7 @@ use smithay::{ use std::cell::Cell; use tracing::warn; -use super::{compositor::client_compositor_state, toplevel_management::ToplevelManagementExt}; +use super::{compositor::client_compositor_state, toplevel_management::minimize_rectangle}; pub mod popup; @@ -37,13 +38,14 @@ pub type PopupGrabData = Cell>>; impl XdgShellHandler for State { fn xdg_shell_state(&mut self) -> &mut XdgShellState { - &mut self.common.shell.xdg_shell_state + &mut self.common.xdg_shell_state } fn new_toplevel(&mut self, surface: ToplevelSurface) { - let seat = self.common.shell.seats.last_active().clone(); + let mut shell = self.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); let window = CosmicSurface::from(surface); - self.common.shell.pending_windows.push((window, seat, None)); + shell.pending_windows.push((window, seat, None)); // We will position the window after the first commit, when we know its size hints } @@ -55,11 +57,14 @@ impl XdgShellHandler for State { if surface.get_parent_surface().is_some() { // let other shells deal with their popups - self.common.shell.unconstrain_popup(&surface); + self.common + .shell + .read() + .unwrap() + .unconstrain_popup(&surface); if surface.send_configure().is_ok() { self.common - .shell .popups .track_popup(PopupKind::from(surface)) .unwrap(); @@ -70,16 +75,18 @@ impl XdgShellHandler for State { fn grab(&mut self, surface: PopupSurface, seat: WlSeat, serial: Serial) { let seat = Seat::from_resource(&seat).unwrap(); let kind = PopupKind::Xdg(surface); - if let Some(root) = find_popup_root_surface(&kind) - .ok() - .and_then(|root| self.common.shell.element_for_surface(&root)) - { - let target = root.clone().into(); - let ret = self - .common + let maybe_root = find_popup_root_surface(&kind).ok().and_then(|root| { + self.common .shell - .popups - .grab_popup(target, kind, &seat, serial); + .read() + .unwrap() + .element_for_surface(&root) + .cloned() + }); + + if let Some(root) = maybe_root { + let target = root.into(); + let ret = self.common.popups.grab_popup(target, kind, &seat, serial); if let Ok(mut grab) = ret { if let Some(keyboard) = seat.get_keyboard() { @@ -128,7 +135,11 @@ impl XdgShellHandler for State { state.positioner = positioner; }); - self.common.shell.unconstrain_popup(&surface); + self.common + .shell + .read() + .unwrap() + .unconstrain_popup(&surface); surface.send_repositioned(token); if let Err(err) = surface.send_configure() { warn!( @@ -140,14 +151,26 @@ impl XdgShellHandler for State { fn move_request(&mut self, surface: ToplevelSurface, seat: WlSeat, serial: Serial) { let seat = Seat::from_resource(&seat).unwrap(); - Shell::move_request( - self, + let mut shell = self.common.shell.write().unwrap(); + if let Some((grab, focus)) = shell.move_request( surface.wl_surface(), &seat, serial, ReleaseMode::NoMouseButtons, false, - ) + &self.common.config, + &self.common.event_loop_handle, + &self.common.xdg_activation_state, + ) { + std::mem::drop(shell); + if grab.is_touch_grab() { + seat.get_touch().unwrap().set_grab(self, grab, serial); + } else { + seat.get_pointer() + .unwrap() + .set_grab(self, grab, serial, focus) + } + } } fn resize_request( @@ -158,70 +181,60 @@ impl XdgShellHandler for State { edges: xdg_toplevel::ResizeEdge, ) { let seat = Seat::from_resource(&seat).unwrap(); - Shell::resize_request(self, surface.wl_surface(), &seat, serial, edges.into()) + let mut shell = self.common.shell.write().unwrap(); + if let Some((grab, focus)) = + shell.resize_request(surface.wl_surface(), &seat, serial, edges.into()) + { + std::mem::drop(shell); + if grab.is_touch_grab() { + seat.get_touch().unwrap().set_grab(self, grab, serial) + } else { + seat.get_pointer() + .unwrap() + .set_grab(self, grab, serial, focus) + } + } } fn minimize_request(&mut self, surface: ToplevelSurface) { - if let Some(mapped) = self - .common - .shell - .element_for_surface(surface.wl_surface()) - .cloned() - { + let mut shell = self.common.shell.write().unwrap(); + if let Some(mapped) = shell.element_for_surface(surface.wl_surface()).cloned() { if !mapped.is_stack() || mapped.active_window().wl_surface().as_ref() == Some(surface.wl_surface()) { - self.common.shell.minimize_request(&mapped) + shell.minimize_request(&mapped) } } } fn maximize_request(&mut self, surface: ToplevelSurface) { - if let Some(mapped) = self - .common - .shell - .element_for_surface(surface.wl_surface()) - .cloned() - { - let seat = self.common.shell.seats.last_active().clone(); - self.common.shell.maximize_request(&mapped, &seat) + let mut shell = self.common.shell.write().unwrap(); + if let Some(mapped) = shell.element_for_surface(surface.wl_surface()).cloned() { + let seat = shell.seats.last_active().clone(); + shell.maximize_request(&mapped, &seat) } } fn unmaximize_request(&mut self, surface: ToplevelSurface) { - if let Some(mapped) = self - .common - .shell - .element_for_surface(surface.wl_surface()) - .cloned() - { - self.common.shell.unmaximize_request(&mapped); + let mut shell = self.common.shell.write().unwrap(); + if let Some(mapped) = shell.element_for_surface(surface.wl_surface()).cloned() { + shell.unmaximize_request(&mapped); } } fn fullscreen_request(&mut self, surface: ToplevelSurface, output: Option) { - let seat = self.common.shell.seats.last_active().clone(); + let mut shell = self.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); let active_output = seat.active_output(); let output = output .as_ref() .and_then(Output::from_resource) .unwrap_or_else(|| active_output.clone()); - if let Some(mapped) = self - .common - .shell - .element_for_surface(surface.wl_surface()) - .cloned() - { - let from = self - .common - .shell - .toplevel_management_state - .minimize_rectangle(&output, &mapped.active_window()); + if let Some(mapped) = shell.element_for_surface(surface.wl_surface()).cloned() { + let from = minimize_rectangle(&output, &mapped.active_window()); - if let Some(set) = self - .common - .shell + if let Some(set) = shell .workspaces .sets .values_mut() @@ -248,19 +261,13 @@ impl XdgShellHandler for State { mapped }; - let workspace_handle = self.common.shell.active_space(&output).handle.clone(); + let workspace_handle = shell.active_space(&output).handle.clone(); for (window, _) in mapped.windows() { - self.common - .shell - .toplevel_info_state - .toplevel_enter_output(&window, &output); - self.common - .shell - .toplevel_info_state - .toplevel_enter_workspace(&window, &workspace_handle); + toplevel_enter_output(&window, &output); + toplevel_enter_workspace(&window, &workspace_handle); } - let workspace = self.common.shell.active_space_mut(&output); + let workspace = shell.active_space_mut(&output); workspace.floating_layer.map(mapped.clone(), None); workspace.fullscreen_request( @@ -269,7 +276,7 @@ impl XdgShellHandler for State { from, &seat, ); - } else if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { + } else if let Some(workspace) = shell.space_for_mut(&mapped) { if workspace.output != output { let (mapped, layer) = if mapped .stack_ref() @@ -300,19 +307,13 @@ impl XdgShellHandler for State { }; let handle = workspace.handle.clone(); - let workspace_handle = self.common.shell.active_space(&output).handle.clone(); + let workspace_handle = shell.active_space(&output).handle.clone(); for (window, _) in mapped.windows() { - self.common - .shell - .toplevel_info_state - .toplevel_enter_output(&window, &output); - self.common - .shell - .toplevel_info_state - .toplevel_enter_workspace(&window, &workspace_handle); + toplevel_enter_output(&window, &output); + toplevel_enter_workspace(&window, &workspace_handle); } - let workspace = self.common.shell.active_space_mut(&output); + let workspace = shell.active_space_mut(&output); workspace.floating_layer.map(mapped.clone(), None); workspace.fullscreen_request( @@ -330,9 +331,7 @@ impl XdgShellHandler for State { } } } else { - if let Some(o) = self - .common - .shell + if let Some(o) = shell .pending_windows .iter_mut() .find(|(s, _, _)| s.wl_surface().as_ref() == Some(surface.wl_surface())) @@ -344,29 +343,23 @@ impl XdgShellHandler for State { } fn unfullscreen_request(&mut self, surface: ToplevelSurface) { - if let Some(mapped) = self - .common - .shell - .element_for_surface(surface.wl_surface()) - .cloned() - { - if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { + let mut shell = self.common.shell.write().unwrap(); + if let Some(mapped) = shell.element_for_surface(surface.wl_surface()).cloned() { + if let Some(workspace) = shell.space_for_mut(&mapped) { let (window, _) = mapped .windows() .find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface())) .unwrap(); if let Some((layer, previous_workspace)) = workspace.unfullscreen_request(&window) { let old_handle = workspace.handle.clone(); - let new_workspace_handle = self - .common - .shell + let new_workspace_handle = shell .workspaces .space_for_handle(&previous_workspace) .is_some() .then_some(previous_workspace) .unwrap_or(old_handle); // if the workspace doesn't exist anymore, we can still remap on the right layer - self.common.shell.remap_unfullscreened_window( + shell.remap_unfullscreened_window( mapped, &old_handle, &new_workspace_handle, @@ -378,20 +371,26 @@ impl XdgShellHandler for State { } fn toplevel_destroyed(&mut self, surface: ToplevelSurface) { - let seat = self.common.shell.seats.last_active().clone(); - self.common.shell.unmap_surface(surface.wl_surface(), &seat); + let (output, clients) = { + let mut shell = self.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); + shell.unmap_surface( + surface.wl_surface(), + &seat, + &mut self.common.toplevel_info_state, + ); - let output = self - .common - .shell - .visible_output_for_surface(surface.wl_surface()) - .cloned(); - if let Some(output) = output.as_ref() { - self.common.shell.refresh_active_space(output); - } + let output = shell + .visible_output_for_surface(surface.wl_surface()) + .cloned(); + if let Some(output) = output.as_ref() { + shell.refresh_active_space(output, &self.common.xdg_activation_state); + } + + // animations might be unblocked now + (output, shell.update_animations()) + }; - // animations might be unblocked now - let clients = self.common.shell.update_animations(); { let dh = self.common.display_handle.clone(); for client in clients.values() { @@ -418,7 +417,23 @@ impl XdgShellHandler for State { }) .unwrap_or_default() .loc; - Shell::menu_request(self, surface.wl_surface(), &seat, serial, location, false) + + let shell = self.common.shell.read().unwrap(); + let res = shell.menu_request( + surface.wl_surface(), + &seat, + serial, + location, + false, + &self.common.config, + &self.common.event_loop_handle, + ); + if let Some((grab, focus)) = res { + std::mem::drop(shell); + seat.get_pointer() + .unwrap() + .set_grab(self, grab, serial, focus) + } } } diff --git a/src/wayland/handlers/xwayland_keyboard_grab.rs b/src/wayland/handlers/xwayland_keyboard_grab.rs index 69e42e07..85e25b55 100644 --- a/src/wayland/handlers/xwayland_keyboard_grab.rs +++ b/src/wayland/handlers/xwayland_keyboard_grab.rs @@ -11,10 +11,12 @@ impl XWaylandKeyboardGrabHandler for State { let element = self .common .shell + .read() + .unwrap() .workspaces .spaces() - .find_map(|x| x.element_for_surface(surface))?; - Some(KeyboardFocusTarget::Element(element.clone())) + .find_map(|x| x.element_for_surface(surface).cloned())?; + Some(KeyboardFocusTarget::Element(element)) } } delegate_xwayland_keyboard_grab!(State); diff --git a/src/wayland/protocols/toplevel_info.rs b/src/wayland/protocols/toplevel_info.rs index 2e72e95b..ce094ca1 100644 --- a/src/wayland/protocols/toplevel_info.rs +++ b/src/wayland/protocols/toplevel_info.rs @@ -185,6 +185,30 @@ where } } +pub fn toplevel_enter_output(toplevel: &impl Window, output: &Output) { + if let Some(state) = toplevel.user_data().get::() { + state.lock().unwrap().outputs.push(output.clone()); + } +} + +pub fn toplevel_leave_output(toplevel: &impl Window, output: &Output) { + if let Some(state) = toplevel.user_data().get::() { + state.lock().unwrap().outputs.retain(|o| o != output); + } +} + +pub fn toplevel_enter_workspace(toplevel: &impl Window, workspace: &WorkspaceHandle) { + if let Some(state) = toplevel.user_data().get::() { + state.lock().unwrap().workspaces.push(workspace.clone()); + } +} + +pub fn toplevel_leave_workspace(toplevel: &impl Window, workspace: &WorkspaceHandle) { + if let Some(state) = toplevel.user_data().get::() { + state.lock().unwrap().workspaces.retain(|w| w != workspace); + } +} + impl ToplevelInfoState where D: GlobalDispatch @@ -223,30 +247,6 @@ where self.toplevels.push(toplevel.clone()); } - pub fn toplevel_enter_output(&mut self, toplevel: &W, output: &Output) { - if let Some(state) = toplevel.user_data().get::() { - state.lock().unwrap().outputs.push(output.clone()); - } - } - - pub fn toplevel_leave_output(&mut self, toplevel: &W, output: &Output) { - if let Some(state) = toplevel.user_data().get::() { - state.lock().unwrap().outputs.retain(|o| o != output); - } - } - - pub fn toplevel_enter_workspace(&mut self, toplevel: &W, workspace: &WorkspaceHandle) { - if let Some(state) = toplevel.user_data().get::() { - state.lock().unwrap().workspaces.push(workspace.clone()); - } - } - - pub fn toplevel_leave_workspace(&mut self, toplevel: &W, workspace: &WorkspaceHandle) { - if let Some(state) = toplevel.user_data().get::() { - state.lock().unwrap().workspaces.retain(|w| w != workspace); - } - } - pub fn remove_toplevel(&mut self, toplevel: &W) { if let Some(state) = toplevel.user_data().get::() { let mut state_inner = state.lock().unwrap(); diff --git a/src/wayland/protocols/toplevel_management.rs b/src/wayland/protocols/toplevel_management.rs index e5868ae0..b823f55f 100644 --- a/src/wayland/protocols/toplevel_management.rs +++ b/src/wayland/protocols/toplevel_management.rs @@ -71,6 +71,29 @@ where } } +pub fn toplevel_rectangle_for( + window: &impl ManagementWindow, +) -> impl Iterator)> { + if let Some(state) = window.user_data().get::() { + let mut state = state.lock().unwrap(); + state + .rectangles + .retain(|(surface, _)| surface.upgrade().is_ok()); + Some( + state + .rectangles + .iter() + .map(|(surface, rect)| (surface.upgrade().unwrap(), *rect)) + .collect::>() + .into_iter(), + ) + .into_iter() + .flatten() + } else { + None.into_iter().flatten() + } +} + pub struct ToplevelManagerGlobalData { filter: Box Fn(&'a Client) -> bool + Send + Sync>, } @@ -102,30 +125,6 @@ impl ToplevelManagementState { } } - pub fn rectangle_for( - &mut self, - window: &impl ManagementWindow, - ) -> impl Iterator)> { - if let Some(state) = window.user_data().get::() { - let mut state = state.lock().unwrap(); - state - .rectangles - .retain(|(surface, _)| surface.upgrade().is_ok()); - Some( - state - .rectangles - .iter() - .map(|(surface, rect)| (surface.upgrade().unwrap(), *rect)) - .collect::>() - .into_iter(), - ) - .into_iter() - .flatten() - } else { - None.into_iter().flatten() - } - } - pub fn global_id(&self) -> GlobalId { self.global.clone() } diff --git a/src/xwayland.rs b/src/xwayland.rs index 1b6c9a1e..c2d945b0 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -6,14 +6,14 @@ use crate::{ state::State, utils::prelude::*, wayland::handlers::{ - toplevel_management::ToplevelManagementExt, xdg_activation::ActivationContext, + toplevel_management::minimize_rectangle, xdg_activation::ActivationContext, }, }; use smithay::{ backend::drm::DrmNode, desktop::space::SpaceElement, reexports::x11rb::protocol::xproto::Window as X11Window, - utils::{Logical, Point, Rectangle, Size}, + utils::{Logical, Point, Rectangle, Size, SERIAL_COUNTER}, wayland::{ selection::{ data_device::{ @@ -45,7 +45,7 @@ pub struct XWaylandState { impl State { pub fn launch_xwayland(&mut self, render_node: Option) { - if self.common.shell.xwayland_state.is_some() { + if self.common.xwayland_state.is_some() { return; } @@ -89,11 +89,11 @@ impl State { ); } - let xwayland_state = data.common.shell.xwayland_state.as_mut().unwrap(); + let xwayland_state = data.common.xwayland_state.as_mut().unwrap(); xwayland_state.xwm = Some(wm); } XWaylandEvent::Exited => { - if let Some(mut xwayland_state) = data.common.shell.xwayland_state.take() { + if let Some(mut xwayland_state) = data.common.xwayland_state.take() { xwayland_state.xwm = None; } } @@ -118,7 +118,7 @@ impl State { }, ) { Ok(display) => { - self.common.shell.xwayland_state = Some(XWaylandState { + self.common.xwayland_state = Some(XWaylandState { xwayland, xwm: None, display, @@ -134,7 +134,14 @@ impl State { impl Common { fn is_x_focused(&self, xwm: XwmId) -> bool { - if let Some(keyboard) = self.shell.seats.last_active().get_keyboard() { + if let Some(keyboard) = self + .shell + .read() + .unwrap() + .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,22 +153,21 @@ impl Common { } pub fn update_x11_stacking_order(&mut self) { - let active_output = self.shell.seats.last_active().active_output(); + let shell = self.shell.read().unwrap(); + let active_output = shell.seats.last_active().active_output(); if let Some(xwm) = self - .shell .xwayland_state .as_mut() .and_then(|state| state.xwm.as_mut()) { // front to back, given that is how the workspace enumerates - let order = self - .shell + let order = shell .workspaces .sets .iter() .filter(|(output, _)| *output == &active_output) .chain( - self.shell + shell .workspaces .sets .iter() @@ -233,7 +239,6 @@ impl Common { impl XwmHandler for State { fn xwm_state(&mut self, _xwm: XwmId) -> &mut X11Wm { self.common - .shell .xwayland_state .as_mut() .and_then(|state| state.xwm.as_mut()) @@ -249,112 +254,100 @@ impl XwmHandler for State { warn!(?window, ?err, "Failed to send Xwayland Mapped-Event",); } + let mut shell = self.common.shell.write().unwrap(); let startup_id = window.startup_id(); - if self.common.shell.element_for_surface(&window).is_some() { + if shell.element_for_surface(&window).is_some() { return; } - let seat = self.common.shell.seats.last_active().clone(); + let seat = shell.seats.last_active().clone(); if let Some(context) = startup_id .map(XdgActivationToken::from) - .and_then(|token| { - self.common - .shell - .xdg_activation_state - .data_for_token(&token) - }) + .and_then(|token| self.common.xdg_activation_state.data_for_token(&token)) .and_then(|data| data.user_data.get::()) { - self.common.shell.pending_activations.insert( + shell.pending_activations.insert( crate::shell::ActivationKey::X11(window.window_id()), context.clone(), ); } let surface = CosmicSurface::from(window); - self.common - .shell - .pending_windows - .push((surface, seat, None)); + shell.pending_windows.push((surface, seat, None)); } fn map_window_notify(&mut self, _xwm: XwmId, surface: X11Surface) { - if let Some((window, _, _)) = self - .common - .shell + let mut shell = self.common.shell.write().unwrap(); + if let Some((window, _, _)) = shell .pending_windows .iter() .find(|(window, _, _)| window.x11_surface() == Some(&surface)) .cloned() { - if !self - .common - .shell + if !shell .pending_activations .contains_key(&crate::shell::ActivationKey::X11(surface.window_id())) { if let Some(startup_id) = window.x11_surface().and_then(|x| x.startup_id()) { if let Some(context) = self .common - .shell .xdg_activation_state .data_for_token(&XdgActivationToken::from(startup_id)) .and_then(|data| data.user_data.get::()) { - self.common.shell.pending_activations.insert( + shell.pending_activations.insert( crate::shell::ActivationKey::X11(surface.window_id()), context.clone(), ); } } } - if let Some(target) = self.common.shell.map_window( + let res = shell.map_window( &window, + &mut self.common.toplevel_info_state, + &mut self.common.workspace_state, &self.common.event_loop_handle, - &self.common.theme, - ) { - let seat = self.common.shell.seats.last_active().clone(); + ); + if let Some(target) = res { + let seat = shell.seats.last_active().clone(); + std::mem::drop(shell); Shell::set_focus(self, Some(&target), &seat, None); } } } fn mapped_override_redirect_window(&mut self, _xwm: XwmId, window: X11Surface) { - if self - .common - .shell + let mut shell = self.common.shell.write().unwrap(); + if shell .override_redirect_windows .iter() .any(|or| or == &window) { return; } - self.common.shell.map_override_redirect(window) + shell.map_override_redirect(window) } fn unmapped_window(&mut self, _xwm: XwmId, window: X11Surface) { + let mut shell = self.common.shell.write().unwrap(); if window.is_override_redirect() { - self.common - .shell - .override_redirect_windows - .retain(|or| or != &window); + shell.override_redirect_windows.retain(|or| or != &window); } else { - let seat = self.common.shell.seats.last_active().clone(); - self.common.shell.unmap_surface(&window, &seat); + let seat = shell.seats.last_active().clone(); + shell.unmap_surface(&window, &seat, &mut self.common.toplevel_info_state); } let outputs = if let Some(wl_surface) = window.wl_surface() { - self.common - .shell + shell .visible_output_for_surface(&wl_surface) .into_iter() .cloned() .collect::>() } else { - self.common.shell.outputs().cloned().collect::>() + shell.outputs().cloned().collect::>() }; for output in outputs.iter() { - self.common.shell.refresh_active_space(output); + shell.refresh_active_space(output, &self.common.xdg_activation_state); } for output in outputs.into_iter() { @@ -375,17 +368,15 @@ impl XwmHandler for State { ) { // We only allow floating X11 windows to resize themselves. Nothing else let mut current_geo = window.geometry(); - if let Some(mapped) = self.common.shell.element_for_surface(&window) { - let is_floating = self - .common - .shell + let shell = self.common.shell.read().unwrap(); + + if let Some(mapped) = shell.element_for_surface(&window) { + let is_floating = shell .workspaces .sets .values() .any(|set| set.sticky_layer.mapped().any(|m| m == mapped)) - || self - .common - .shell + || shell .space_for(&mapped) .filter(|space| space.is_floating(mapped)) .is_some(); @@ -430,8 +421,9 @@ impl XwmHandler for State { above: Option, ) { if window.is_override_redirect() { + let mut shell = self.common.shell.write().unwrap(); if let Some(id) = above { - let or_windows = &mut self.common.shell.override_redirect_windows; + let or_windows = &mut shell.override_redirect_windows; if let Some(own_pos) = or_windows.iter().position(|or| or == &window) { let compare_pos = or_windows .iter() @@ -445,7 +437,7 @@ impl XwmHandler for State { } let geo = window.geometry().as_global(); - for (output, overlap) in self.common.shell.outputs().cloned().map(|o| { + for (output, overlap) in shell.outputs().cloned().map(|o| { let intersection = o.geometry().intersection(geo); (o, intersection) }) { @@ -466,50 +458,88 @@ impl XwmHandler for State { resize_edge: smithay::xwayland::xwm::ResizeEdge, ) { if let Some(wl_surface) = window.wl_surface() { - let seat = self.common.shell.seats.last_active().clone(); - Shell::resize_request(self, &wl_surface, &seat, None, resize_edge.into()) + let mut shell = self.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); + if let Some((grab, focus)) = + shell.resize_request(&wl_surface, &seat, None, resize_edge.into()) + { + std::mem::drop(shell); + if grab.is_touch_grab() { + seat.get_touch() + .unwrap() + .set_grab(self, grab, SERIAL_COUNTER.next_serial()) + } else { + seat.get_pointer().unwrap().set_grab( + self, + grab, + SERIAL_COUNTER.next_serial(), + focus, + ) + } + } } } fn move_request(&mut self, _xwm: XwmId, window: X11Surface, _button: u32) { if let Some(wl_surface) = window.wl_surface() { - let seat = self.common.shell.seats.last_active().clone(); - Shell::move_request( - self, + let mut shell = self.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); + if let Some((grab, focus)) = shell.move_request( &wl_surface, &seat, None, ReleaseMode::NoMouseButtons, false, - ) + &self.common.config, + &self.common.event_loop_handle, + &self.common.xdg_activation_state, + ) { + std::mem::drop(shell); + if grab.is_touch_grab() { + seat.get_touch() + .unwrap() + .set_grab(self, grab, SERIAL_COUNTER.next_serial()) + } else { + seat.get_pointer().unwrap().set_grab( + self, + grab, + SERIAL_COUNTER.next_serial(), + focus, + ) + } + } } } 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.shell.seats.last_active().clone(); - self.common.shell.maximize_request(&mapped, &seat); + let mut shell = self.common.shell.write().unwrap(); + if let Some(mapped) = shell.element_for_surface(&window).cloned() { + let seat = shell.seats.last_active().clone(); + shell.maximize_request(&mapped, &seat); } } fn unmaximize_request(&mut self, _xwm: XwmId, window: X11Surface) { - if let Some(mapped) = self.common.shell.element_for_surface(&window).cloned() { - self.common.shell.unmaximize_request(&mapped); + let mut shell = self.common.shell.write().unwrap(); + if let Some(mapped) = shell.element_for_surface(&window).cloned() { + shell.unmaximize_request(&mapped); } } fn minimize_request(&mut self, _xwm: XwmId, window: X11Surface) { - if let Some(mapped) = self.common.shell.element_for_surface(&window).cloned() { + let mut shell = self.common.shell.write().unwrap(); + if let Some(mapped) = shell.element_for_surface(&window).cloned() { if !mapped.is_stack() || mapped.active_window().is_window(&window) { - self.common.shell.minimize_request(&mapped); + shell.minimize_request(&mapped); } } } 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.shell.seats.last_active().clone(); - self.common.shell.unminimize_request(&mapped, &seat); + let mut shell = self.common.shell.write().unwrap(); + if let Some(mut mapped) = shell.element_for_surface(&window).cloned() { + let seat = shell.seats.last_active().clone(); + shell.unminimize_request(&mapped, &seat); if mapped.is_stack() { let maybe_surface = mapped.windows().find(|(w, _)| w.is_window(&window)); if let Some((surface, _)) = maybe_surface { @@ -520,11 +550,10 @@ impl XwmHandler for State { } fn fullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) { - 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 - .shell + let mut shell = self.common.shell.write().unwrap(); + let seat = shell.seats.last_active().clone(); + if let Some(mapped) = shell.element_for_surface(&window).cloned() { + if let Some((output, handle)) = shell .space_for(&mapped) .map(|workspace| (workspace.output.clone(), workspace.handle.clone())) { @@ -532,13 +561,8 @@ impl XwmHandler for State { .windows() .find(|(w, _)| w.x11_surface() == Some(&window)) { - let from = self - .common - .shell - .toplevel_management_state - .minimize_rectangle(&output, &surface); - self.common - .shell + let from = minimize_rectangle(&output, &surface); + shell .workspaces .space_for_handle_mut(&handle) .unwrap() @@ -547,9 +571,7 @@ impl XwmHandler for State { } } else { let output = seat.active_output(); - if let Some(o) = self - .common - .shell + if let Some(o) = shell .pending_windows .iter_mut() .find(|(s, _, _)| s.x11_surface() == Some(&window)) @@ -561,8 +583,9 @@ impl XwmHandler for State { } fn unfullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) { - if let Some(mapped) = self.common.shell.element_for_surface(&window).cloned() { - if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { + let mut shell = self.common.shell.write().unwrap(); + if let Some(mapped) = shell.element_for_surface(&window).cloned() { + if let Some(workspace) = shell.space_for_mut(&mapped) { let (window, _) = mapped .windows() .find(|(w, _)| w.x11_surface() == Some(&window)) @@ -580,10 +603,17 @@ impl XwmHandler for State { mime_type: String, fd: OwnedFd, ) { - let seat = self.common.shell.seats.last_active(); + let seat = self + .common + .shell + .read() + .unwrap() + .seats + .last_active() + .clone(); match selection { SelectionTarget::Clipboard => { - if let Err(err) = request_data_device_client_selection(seat, mime_type, fd) { + if let Err(err) = request_data_device_client_selection(&seat, mime_type, fd) { error!( ?err, "Failed to request current wayland clipboard for Xwayland.", @@ -591,7 +621,7 @@ impl XwmHandler for State { } } SelectionTarget::Primary => { - if let Err(err) = request_primary_client_selection(seat, mime_type, fd) { + if let Err(err) = request_primary_client_selection(&seat, mime_type, fd) { error!( ?err, "Failed to request current wayland primary selection for Xwayland.", @@ -609,7 +639,14 @@ impl XwmHandler for State { trace!(?selection, ?mime_types, "Got Selection from Xwayland",); if self.common.is_x_focused(xwm) { - let seat = self.common.shell.seats.last_active(); + let seat = self + .common + .shell + .read() + .unwrap() + .seats + .last_active() + .clone(); match selection { SelectionTarget::Clipboard => { set_data_device_selection(&self.common.display_handle, &seat, mime_types, xwm) @@ -622,7 +659,8 @@ impl XwmHandler for State { } fn cleared_selection(&mut self, xwm: XwmId, selection: SelectionTarget) { - for seat in self.common.shell.seats.iter() { + let shell = self.common.shell.read().unwrap(); + for seat in shell.seats.iter() { match selection { SelectionTarget::Clipboard => { if current_data_device_selection_userdata(seat).as_deref() == Some(&xwm) {