diff --git a/cosmic-comp-config/src/lib.rs b/cosmic-comp-config/src/lib.rs index e5e738830..fd27ea69a 100644 --- a/cosmic-comp-config/src/lib.rs +++ b/cosmic-comp-config/src/lib.rs @@ -66,6 +66,13 @@ impl Default for AppearanceConfig { } } +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum DecorationPreference { + #[default] + ClientSide, + ServerSide, +} + #[derive(Clone, Debug, PartialEq, CosmicConfigEntry)] #[version = 1] pub struct CosmicCompConfig { @@ -102,6 +109,7 @@ pub struct CosmicCompConfig { /// Hide the cursor after this many seconds of pointer inactivity (None disables) pub cursor_hide_timeout: Option, pub activation_policy: ActivationPolicy, + pub decoration_preference: DecorationPreference, } impl Default for CosmicCompConfig { @@ -140,6 +148,7 @@ impl Default for CosmicCompConfig { appearance_settings: AppearanceConfig::default(), cursor_hide_timeout: None, activation_policy: ActivationPolicy::default(), + decoration_preference: DecorationPreference::default(), } } } diff --git a/src/config/mod.rs b/src/config/mod.rs index 1d9b9c921..7c062931a 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -45,8 +45,8 @@ mod types; use cosmic::config::CosmicTk; pub use cosmic_comp_config::EdidProduct; use cosmic_comp_config::{ - ActivationPolicy, AppearanceConfig, CosmicCompConfig, KeyboardConfig, TileBehavior, XkbConfig, - XwaylandDescaling, XwaylandEavesdropping, ZoomConfig, + ActivationPolicy, AppearanceConfig, CosmicCompConfig, DecorationPreference, KeyboardConfig, + TileBehavior, XkbConfig, XwaylandDescaling, XwaylandEavesdropping, ZoomConfig, input::{DeviceState as InputDeviceState, InputConfig, TouchpadOverride}, output::comp::{ OutputConfig, OutputInfo, OutputState, OutputsConfig, TransformDef, load_outputs, @@ -972,6 +972,13 @@ fn config_changed(config: cosmic_config::Config, keys: Vec, state: &mut state.common.config.cosmic_conf.activation_policy = new; } } + "decoration_preference" => { + let new = get_config::(&config, "decoration_preference"); + if new != state.common.config.cosmic_conf.decoration_preference { + state.common.config.cosmic_conf.decoration_preference = new; + state.update_decorations(); + } + } _ => {} } } diff --git a/src/wayland/handlers/decoration.rs b/src/wayland/handlers/decoration.rs index 90ec5855e..ae58d3380 100644 --- a/src/wayland/handlers/decoration.rs +++ b/src/wayland/handlers/decoration.rs @@ -1,5 +1,6 @@ use std::{cell::RefCell, sync::Mutex}; +use cosmic_comp_config::DecorationPreference; use smithay::{ desktop::Window, reexports::{ @@ -20,7 +21,7 @@ use smithay::{ }; use wayland_backend::protocol::WEnum; -use crate::state::State; +use crate::{shell::CosmicMapped, state::State}; pub struct PreferredDecorationMode(RefCell>); @@ -29,7 +30,7 @@ impl PreferredDecorationMode { window .user_data() .get::() - .is_none() + .is_none_or(|mode| mode.0.borrow().is_none()) } pub fn mode(window: &Window) -> Option { @@ -53,6 +54,73 @@ impl PreferredDecorationMode { } } +impl State { + pub fn default_decoration(&self) -> DecorationPreference { + self.common.config.cosmic_conf.decoration_preference + } + + pub fn update_decorations(&self) { + let shell = self.common.shell.read(); + + let update = |mapped: &CosmicMapped| { + let mode = if mapped.is_stack() { + XdgMode::ServerSide + } else { + XdgMode::from_preference(self.default_decoration()) + }; + for (window, _) in mapped.windows() { + if PreferredDecorationMode::is_unset(&window.0) { + if let Some(toplevel) = window.0.toplevel() { + if toplevel.with_committed_state(|state| { + state.is_some_and(|state| state.decoration_mode.is_some()) + }) { + toplevel.with_pending_state(|state| { + state.decoration_mode = Some(mode); + }); + toplevel.send_configure(); + } + } + } + } + }; + + for set in shell.workspaces.sets.values() { + set.sticky_layer.mapped().for_each(update); + } + + for space in shell.workspaces.spaces() { + space.mapped().for_each(update); + space + .minimized_windows + .iter() + .filter_map(|m| m.mapped()) + .for_each(update); + } + } +} + +trait FromDecorationPreference { + fn from_preference(preference: DecorationPreference) -> Self; +} + +impl FromDecorationPreference for XdgMode { + fn from_preference(preference: DecorationPreference) -> Self { + match preference { + DecorationPreference::ClientSide => XdgMode::ClientSide, + DecorationPreference::ServerSide => XdgMode::ServerSide, + } + } +} + +impl FromDecorationPreference for KdeMode { + fn from_preference(preference: DecorationPreference) -> Self { + match preference { + DecorationPreference::ClientSide => KdeMode::Client, + DecorationPreference::ServerSide => KdeMode::Server, + } + } +} + pub type KdeDecorationData = Mutex; #[derive(Debug, Default)] pub struct KdeDecorationSurfaceState { @@ -67,7 +135,7 @@ impl XdgDecorationHandler for State { let mode = if mapped.is_stack() { XdgMode::ServerSide } else { - XdgMode::ClientSide + XdgMode::from_preference(self.default_decoration()) }; if let Some((window, _)) = mapped @@ -80,6 +148,10 @@ impl XdgDecorationHandler for State { }); toplevel.send_configure(); } + } else { + toplevel.with_pending_state(|state| { + state.decoration_mode = Some(XdgMode::from_preference(self.default_decoration())) + }) } } @@ -99,6 +171,13 @@ impl XdgDecorationHandler for State { } } else { toplevel.with_pending_state(|state| state.decoration_mode = Some(mode)); + if let Some(pending) = shell + .pending_windows + .iter() + .find(|pending| pending.surface.0.toplevel().is_some_and(|t| t == &toplevel)) + { + PreferredDecorationMode::update(&pending.surface.0, Some(mode)); + } } } @@ -110,11 +189,28 @@ impl XdgDecorationHandler for State { .find(|(window, _)| window.wl_surface().as_deref() == Some(toplevel.wl_surface())) && let Some(toplevel) = window.0.toplevel() { + let mode = if mapped.is_stack() { + XdgMode::ServerSide + } else { + XdgMode::from_preference(self.default_decoration()) + }; + PreferredDecorationMode::update(&window.0, None); toplevel.with_pending_state(|state| { - state.decoration_mode = None; + state.decoration_mode = Some(mode); }); toplevel.send_configure(); + } else { + toplevel.with_pending_state(|state| { + state.decoration_mode = Some(XdgMode::from_preference(self.default_decoration())) + }); + if let Some(pending) = shell + .pending_windows + .iter() + .find(|pending| pending.surface.0.toplevel().is_some_and(|t| t == &toplevel)) + { + PreferredDecorationMode::update(&pending.surface.0, None); + } } } } @@ -129,10 +225,10 @@ impl KdeDecorationHandler for State { if mapped.is_stack() { KdeMode::Server } else { - KdeMode::Client + KdeMode::from_preference(self.default_decoration()) } } else { - KdeMode::Client + KdeMode::from_preference(self.default_decoration()) }; with_states(surface, |states| {