Apply window rules to xwayland windows

This commit is contained in:
Ottatop 2023-09-07 18:00:58 -05:00
parent 178ee9464b
commit 5a9f15a4d4
5 changed files with 161 additions and 154 deletions

View file

@ -98,7 +98,7 @@ require("pinnacle").setup(function(pinnacle)
rule = { size = { 300, 300 }, location = { 50, 50 } },
}, {
cond = {
cond_all = { { class = "Alacritty" }, { tag = "4" } },
cond_all = { { class = "XTerm" }, { tag = "4" } },
},
rule = { size = { 500, 500 }, floating_or_tiled = "Floating" },
})

View file

@ -2,8 +2,6 @@
use std::num::NonZeroU32;
use smithay::wayland::{compositor, shell::xdg::XdgToplevelSurfaceData};
use crate::{
output::OutputName,
state::{State, WithState},
@ -36,42 +34,8 @@ impl WindowRuleCondition {
WindowRuleCondition::CondAll(conds) => {
conds.iter().all(|cond| Self::is_met(cond, state, window))
}
WindowRuleCondition::Class(class) => {
let Some(wl_surf) = window.wl_surface() else {
return false;
};
let current_class = compositor::with_states(&wl_surf, |states| {
states
.data_map
.get::<XdgToplevelSurfaceData>()
.expect("XdgToplevelSurfaceData wasn't in surface's data map")
.lock()
.expect("Failed to lock Mutex<XdgToplevelSurfaceData>")
.app_id
.clone()
});
current_class.as_ref() == Some(class)
}
WindowRuleCondition::Title(title) => {
let Some(wl_surf) = window.wl_surface() else {
return false;
};
let current_title = compositor::with_states(&wl_surf, |states| {
states
.data_map
.get::<XdgToplevelSurfaceData>()
.expect("XdgToplevelSurfaceData wasn't in surface's data map")
.lock()
.expect("Failed to lock Mutex<XdgToplevelSurfaceData>")
.title
.clone()
});
current_title.as_ref() == Some(title)
}
WindowRuleCondition::Class(class) => window.class().as_ref() == Some(class),
WindowRuleCondition::Title(title) => window.title().as_ref() == Some(title),
WindowRuleCondition::Tag(tag) => {
let Some(tag) = tag.tag(state) else {
tracing::warn!("WindowRuleCondition no tag");

View file

@ -1,8 +1,8 @@
use smithay::{
delegate_xdg_shell,
desktop::{
find_popup_root_surface, layer_map_for_output, space::SpaceElement, PopupKeyboardGrab,
PopupKind, PopupPointerGrab, PopupUngrabStrategy, Window, WindowSurfaceType,
find_popup_root_surface, layer_map_for_output, PopupKeyboardGrab, PopupKind,
PopupPointerGrab, PopupUngrabStrategy, Window, WindowSurfaceType,
},
input::{pointer::Focus, Seat},
output::Output,
@ -13,7 +13,7 @@ use smithay::{
Resource,
},
},
utils::{Point, Rectangle, Serial, SERIAL_COUNTER},
utils::{Serial, SERIAL_COUNTER},
wayland::{
compositor::{self, CompositorHandler},
shell::xdg::{
@ -24,13 +24,9 @@ use smithay::{
};
use crate::{
api::msg::window_rules::{self, WindowRule},
focus::FocusTarget,
state::{State, WithState},
window::{
window_state::{FloatingOrTiled, LocationRequestState},
WindowElement, BLOCKER_COUNTER,
},
window::{window_state::LocationRequestState, WindowElement, BLOCKER_COUNTER},
};
impl XdgShellHandler for State {
@ -116,110 +112,7 @@ impl XdgShellHandler for State {
}
},
|data| {
for (cond, rule) in data.state.window_rules.iter() {
if cond.is_met(&data.state, &window) {
let WindowRule {
output,
tags,
floating_or_tiled,
fullscreen_or_maximized,
size,
location,
} = rule;
// TODO: If both `output` and `tags` are specified, `tags` will apply over
// | `output`.
if let Some(output_name) = output {
if let Some(output) = output_name.output(&data.state) {
let tags = output.with_state(|state| {
state.focused_tags().cloned().collect::<Vec<_>>()
});
window.with_state(|state| state.tags = tags.clone());
}
}
if let Some(tag_ids) = tags {
let tags = tag_ids
.iter()
.filter_map(|tag_id| tag_id.tag(&data.state))
.collect::<Vec<_>>();
window.with_state(|state| state.tags = tags.clone());
}
if let Some(floating_or_tiled) = floating_or_tiled {
match floating_or_tiled {
window_rules::FloatingOrTiled::Floating => {
if window.with_state(|state| state.floating_or_tiled.is_tiled())
{
window.toggle_floating();
}
}
window_rules::FloatingOrTiled::Tiled => {
if window
.with_state(|state| state.floating_or_tiled.is_floating())
{
window.toggle_floating();
}
}
}
}
if let Some(fs_or_max) = fullscreen_or_maximized {
window.with_state(|state| state.fullscreen_or_maximized = *fs_or_max);
}
if let Some((w, h)) = size {
let mut window_size = window.geometry().size;
window_size.w = u32::from(*w) as i32;
window_size.h = u32::from(*h) as i32;
match window.with_state(|state| state.floating_or_tiled) {
FloatingOrTiled::Floating(mut rect) => {
rect.size = (u32::from(*w) as i32, u32::from(*h) as i32).into();
window.with_state(|state| {
state.floating_or_tiled = FloatingOrTiled::Floating(rect)
});
}
FloatingOrTiled::Tiled(mut rect) => {
if let Some(rect) = rect.as_mut() {
rect.size =
(u32::from(*w) as i32, u32::from(*h) as i32).into();
}
window.with_state(|state| {
state.floating_or_tiled = FloatingOrTiled::Tiled(rect)
});
}
}
}
if let Some(loc) = location {
match window.with_state(|state| state.floating_or_tiled) {
FloatingOrTiled::Floating(mut rect) => {
rect.loc = (*loc).into();
window.with_state(|state| {
state.floating_or_tiled = FloatingOrTiled::Floating(rect)
});
data.state.space.map_element(window.clone(), *loc, false);
}
FloatingOrTiled::Tiled(rect) => {
// If the window is tiled, don't set the size. Instead, set
// what the size will be when it gets set to floating.
let rect = rect.unwrap_or_else(|| {
let size = window.geometry().size;
Rectangle::from_loc_and_size(Point::from(*loc), size)
});
window.with_state(|state| {
state.floating_or_tiled = FloatingOrTiled::Tiled(Some(rect))
});
}
}
}
}
}
data.state.apply_window_rules(&window);
if let Some(focused_output) = data.state.focus_state.focused_output.clone() {
data.state.update_windows(&focused_output);

View file

@ -154,6 +154,11 @@ impl XwmHandler for CalloopData {
.collect::<Vec<_>>();
self.state.windows.push(window.clone());
self.state.focus_state.set_focus(window.clone());
self.state.apply_window_rules(&window);
if let Some(focused_output) = self.state.focus_state.focused_output.clone() {
self.state.update_windows(&focused_output);
BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst);

View file

@ -25,16 +25,20 @@ use smithay::{
},
utils::{user_data::UserDataMap, IsAlive, Logical, Point, Rectangle, Serial, Size},
wayland::{
compositor::{Blocker, BlockerState, SurfaceData},
compositor::{self, Blocker, BlockerState, SurfaceData},
dmabuf::DmabufFeedback,
seat::WaylandFocus,
shell::xdg::XdgToplevelSurfaceData,
},
xwayland::X11Surface,
};
use crate::state::{State, WithState};
use crate::{
api::msg::window_rules::{self, WindowRule},
state::{State, WithState},
};
use self::window_state::{LocationRequestState, WindowElementState};
use self::window_state::{FloatingOrTiled, LocationRequestState, WindowElementState};
pub mod window_state;
@ -232,6 +236,42 @@ impl WindowElement {
}
}
pub fn class(&self) -> Option<String> {
match self {
WindowElement::Wayland(window) => {
compositor::with_states(window.toplevel().wl_surface(), |states| {
states
.data_map
.get::<XdgToplevelSurfaceData>()
.expect("XdgToplevelSurfaceData wasn't in surface's data map")
.lock()
.expect("Failed to lock Mutex<XdgToplevelSurfaceData>")
.app_id
.clone()
})
}
WindowElement::X11(surface) => Some(surface.class()),
}
}
pub fn title(&self) -> Option<String> {
match self {
WindowElement::Wayland(window) => {
compositor::with_states(window.toplevel().wl_surface(), |states| {
states
.data_map
.get::<XdgToplevelSurfaceData>()
.expect("XdgToplevelSurfaceData wasn't in surface's data map")
.lock()
.expect("Failed to lock Mutex<XdgToplevelSurfaceData>")
.title
.clone()
})
}
WindowElement::X11(surface) => Some(surface.title()),
}
}
/// Get the output this window is on.
///
/// This method gets the first tag the window has and returns its output.
@ -499,3 +539,108 @@ impl Blocker for WindowBlocker {
}
}
}
impl State {
pub fn apply_window_rules(&mut self, window: &WindowElement) {
tracing::debug!("Applying window rules");
for (cond, rule) in self.window_rules.iter() {
if cond.is_met(self, window) {
let WindowRule {
output,
tags,
floating_or_tiled,
fullscreen_or_maximized,
size,
location,
} = rule;
// TODO: If both `output` and `tags` are specified, `tags` will apply over
// | `output`.
if let Some(output_name) = output {
if let Some(output) = output_name.output(self) {
let tags = output
.with_state(|state| state.focused_tags().cloned().collect::<Vec<_>>());
window.with_state(|state| state.tags = tags.clone());
}
}
if let Some(tag_ids) = tags {
let tags = tag_ids
.iter()
.filter_map(|tag_id| tag_id.tag(self))
.collect::<Vec<_>>();
window.with_state(|state| state.tags = tags.clone());
}
if let Some(floating_or_tiled) = floating_or_tiled {
match floating_or_tiled {
window_rules::FloatingOrTiled::Floating => {
if window.with_state(|state| state.floating_or_tiled.is_tiled()) {
window.toggle_floating();
}
}
window_rules::FloatingOrTiled::Tiled => {
if window.with_state(|state| state.floating_or_tiled.is_floating()) {
window.toggle_floating();
}
}
}
}
if let Some(fs_or_max) = fullscreen_or_maximized {
window.with_state(|state| state.fullscreen_or_maximized = *fs_or_max);
}
if let Some((w, h)) = size {
let mut window_size = window.geometry().size;
window_size.w = u32::from(*w) as i32;
window_size.h = u32::from(*h) as i32;
match window.with_state(|state| state.floating_or_tiled) {
FloatingOrTiled::Floating(mut rect) => {
rect.size = (u32::from(*w) as i32, u32::from(*h) as i32).into();
window.with_state(|state| {
state.floating_or_tiled = FloatingOrTiled::Floating(rect)
});
}
FloatingOrTiled::Tiled(mut rect) => {
if let Some(rect) = rect.as_mut() {
rect.size = (u32::from(*w) as i32, u32::from(*h) as i32).into();
}
window.with_state(|state| {
state.floating_or_tiled = FloatingOrTiled::Tiled(rect)
});
}
}
}
if let Some(loc) = location {
match window.with_state(|state| state.floating_or_tiled) {
FloatingOrTiled::Floating(mut rect) => {
rect.loc = (*loc).into();
window.with_state(|state| {
state.floating_or_tiled = FloatingOrTiled::Floating(rect)
});
self.space.map_element(window.clone(), *loc, false);
}
FloatingOrTiled::Tiled(rect) => {
// If the window is tiled, don't set the size. Instead, set
// what the size will be when it gets set to floating.
let rect = rect.unwrap_or_else(|| {
let size = window.geometry().size;
Rectangle::from_loc_and_size(Point::from(*loc), size)
});
window.with_state(|state| {
state.floating_or_tiled = FloatingOrTiled::Tiled(Some(rect))
});
}
}
}
}
}
}
}