diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/handlers/xdg_shell.rs | 18 | ||||
| -rw-r--r-- | src/niri.rs | 19 | ||||
| -rw-r--r-- | src/utils/mod.rs | 65 | ||||
| -rw-r--r-- | src/window/mapped.rs | 7 | ||||
| -rw-r--r-- | src/window/mod.rs | 7 |
5 files changed, 103 insertions, 13 deletions
diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index 840dd599..0c757c92 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -45,7 +45,9 @@ use crate::input::touch_resize_grab::TouchResizeGrab; use crate::input::{PointerOrTouchStartData, DOUBLE_CLICK_TIME}; use crate::niri::{PopupGrabState, State}; use crate::utils::transaction::Transaction; -use crate::utils::{get_monotonic_time, output_matches_name, send_scale_transform, ResizeEdge}; +use crate::utils::{ + get_monotonic_time, output_matches_name, send_scale_transform, update_tiled_state, ResizeEdge, +}; use crate::window::{InitialConfigureState, ResolvedWindowRules, Unmapped, WindowRef}; impl XdgShellHandler for State { @@ -773,7 +775,7 @@ impl XdgDecorationHandler for State { delegate_xdg_decoration!(State); /// Whether KDE server decorations are in use. -#[derive(Default)] +#[derive(Default, Clone)] pub struct KdeDecorationsModeState { server: Cell<bool>, } @@ -946,16 +948,8 @@ impl State { ); } - // If the user prefers no CSD, it's a reasonable assumption that they would prefer to get - // rid of the various client-side rounded corners also by using the tiled state. - if config.prefer_no_csd { - toplevel.with_pending_state(|state| { - state.states.set(xdg_toplevel::State::TiledLeft); - state.states.set(xdg_toplevel::State::TiledRight); - state.states.set(xdg_toplevel::State::TiledTop); - state.states.set(xdg_toplevel::State::TiledBottom); - }); - } + // Set the tiled state for the initial configure. + update_tiled_state(toplevel, config.prefer_no_csd, rules.tiled_state); // Set the configured settings. *state = InitialConfigureState::Configured { diff --git a/src/niri.rs b/src/niri.rs index 6ce11e59..56582d15 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -646,6 +646,10 @@ impl State { self.refresh_popup_grab(); self.update_keyboard_focus(); + // Should be called before refresh_layout() because that one will refresh other window + // states and then send a pending configure. + self.niri.refresh_window_states(); + // Needs to be called after updating the keyboard focus. self.niri.refresh_layout(); @@ -3255,6 +3259,16 @@ impl Niri { self.idle_notifier_state.set_is_inhibited(is_inhibited); } + pub fn refresh_window_states(&mut self) { + let _span = tracy_client::span!("Niri::refresh_window_states"); + + let config = self.config.borrow(); + self.layout.with_windows_mut(|mapped, _output| { + mapped.update_tiled_state(config.prefer_no_csd); + }); + drop(config); + } + pub fn refresh_window_rules(&mut self) { let _span = tracy_client::span!("Niri::refresh_window_rules"); @@ -3270,6 +3284,11 @@ impl Niri { if let Some(output) = output { outputs.insert(output.clone()); } + + // Since refresh_window_rules() is called after refresh_layout(), we need to update + // the tiled state right here, so that it's picked up by the following + // send_pending_configure(). + mapped.update_tiled_state(config.prefer_no_csd); } }); drop(config); diff --git a/src/utils/mod.rs b/src/utils/mod.rs index e43bad89..2f16e855 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -15,6 +15,7 @@ use niri_config::{Config, OutputName}; use smithay::input::pointer::CursorIcon; use smithay::output::{self, Output}; use smithay::reexports::rustix::time::{clock_gettime, ClockId}; +use smithay::reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1; use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel; use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; use smithay::reexports::wayland_server::{DisplayHandle, Resource as _}; @@ -26,6 +27,7 @@ use smithay::wayland::shell::xdg::{ }; use wayland_backend::server::Credentials; +use crate::handlers::KdeDecorationsModeState; use crate::niri::ClientState; pub mod id; @@ -268,6 +270,69 @@ pub fn with_toplevel_role<T>( }) } +pub fn update_tiled_state( + toplevel: &ToplevelSurface, + prefer_no_csd: bool, + force_tiled: Option<bool>, +) { + // Determine the default value for our tiled state. The idea is to use the tiled state to + // make windows rectangular even if they don't support xdg-decoration (e.g. GTK). + // + // If the user prefers no CSD, it's a reasonable assumption that they would prefer to get + // rid of the various client-side rounded corners also by using the tiled state. + let should_tile = || { + // Figure out if the client bound any decoration globals for this window. In this case, + // the pending decoration mode will be set to something (we always set it upon binding the + // global and never reset to None). + // + // If the client bound a decoration global, use the mode that we negotiated. This way, + // changing the decoration mode on the client at runtime will synchonize with the + // default tiled state. + if let Some(mode) = toplevel.with_pending_state(|state| state.decoration_mode) { + mode == zxdg_toplevel_decoration_v1::Mode::ServerSide + } else if let Some(mode) = with_states(toplevel.wl_surface(), |states| { + states.data_map.get::<KdeDecorationsModeState>().cloned() + }) { + // Actually, make the KDE decoration overridable with prefer_no_csd. GTK 3 likes to + // always request CSD through it, and we want prefer_no_csd to set the tiled state + // automatically for GTK 3. Also, unlike xdg-decoration, KDE decoration is not + // synchronized to commits, so that argument is less important. + mode.is_server() || prefer_no_csd + } else { + // The client doesn't see or doesn't care about the decoration protocols. In this + // case, use the current prefer_no_csd value as the user's intention. + // + // This is a bit weird because it makes it seem like prefer_no_csd can apply live, + // while that isn't really the case. That's because prefer_no_csd controls two separate + // things: whether the client sees the decoration globals, and the tiled state. + // + // A more accurate way would perhaps be to check if the client cannot see the + // decoration globals, and in this case behave as if prefer_no_csd was false. However, + // this also regresses the common case of GTK 4 applications that do not react to + // xdg-decoration in any way, and therefore the tiled state *is* the "no CSD" mode from + // the user's perspective, so by artificially gating it we would artificially make it + // impossible to apply it live for GTK 4 applications. + prefer_no_csd + } + }; + + let should_tile = force_tiled.unwrap_or_else(should_tile); + + toplevel.with_pending_state(|state| { + if should_tile { + state.states.set(xdg_toplevel::State::TiledLeft); + state.states.set(xdg_toplevel::State::TiledRight); + state.states.set(xdg_toplevel::State::TiledTop); + state.states.set(xdg_toplevel::State::TiledBottom); + } else { + state.states.unset(xdg_toplevel::State::TiledLeft); + state.states.unset(xdg_toplevel::State::TiledRight); + state.states.unset(xdg_toplevel::State::TiledTop); + state.states.unset(xdg_toplevel::State::TiledBottom); + } + }); +} + pub fn get_credentials_for_surface(surface: &WlSurface) -> Option<Credentials> { let handle = surface.handle().upgrade()?; let dh = DisplayHandle::from(handle); diff --git a/src/window/mapped.rs b/src/window/mapped.rs index 6b539e46..ac508990 100644 --- a/src/window/mapped.rs +++ b/src/window/mapped.rs @@ -35,7 +35,8 @@ use crate::render_helpers::{BakedBuffer, RenderTarget, SplitElements}; use crate::utils::id::IdCounter; use crate::utils::transaction::Transaction; use crate::utils::{ - get_credentials_for_surface, send_scale_transform, with_toplevel_role, ResizeEdge, + get_credentials_for_surface, send_scale_transform, update_tiled_state, with_toplevel_role, + ResizeEdge, }; #[derive(Debug)] @@ -462,6 +463,10 @@ impl Mapped { }; self.window.send_frame(output, time, throttle, should_send); } + + pub fn update_tiled_state(&self, prefer_no_csd: bool) { + update_tiled_state(self.toplevel(), prefer_no_csd, self.rules.tiled_state); + } } impl Drop for Mapped { diff --git a/src/window/mod.rs b/src/window/mod.rs index c99d10d2..7c0da71b 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -111,6 +111,9 @@ pub struct ResolvedWindowRules { /// Multiplier for all scroll events sent to this window. pub scroll_factor: Option<f64>, + + /// Override whether to set the Tiled xdg-toplevel state on the window. + pub tiled_state: Option<bool>, } impl<'a> WindowRef<'a> { @@ -217,6 +220,7 @@ impl ResolvedWindowRules { block_out_from: None, variable_refresh_rate: None, scroll_factor: None, + tiled_state: None, } } @@ -335,6 +339,9 @@ impl ResolvedWindowRules { if let Some(x) = rule.scroll_factor { resolved.scroll_factor = Some(x.0); } + if let Some(x) = rule.tiled_state { + resolved.tiled_state = Some(x); + } } resolved.open_on_output = open_on_output.map(|x| x.to_owned()); |
