mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-28 19:58:27 +01:00
Apply window rules to xwayland windows
This commit is contained in:
parent
178ee9464b
commit
5a9f15a4d4
5 changed files with 161 additions and 154 deletions
|
@ -98,7 +98,7 @@ require("pinnacle").setup(function(pinnacle)
|
||||||
rule = { size = { 300, 300 }, location = { 50, 50 } },
|
rule = { size = { 300, 300 }, location = { 50, 50 } },
|
||||||
}, {
|
}, {
|
||||||
cond = {
|
cond = {
|
||||||
cond_all = { { class = "Alacritty" }, { tag = "4" } },
|
cond_all = { { class = "XTerm" }, { tag = "4" } },
|
||||||
},
|
},
|
||||||
rule = { size = { 500, 500 }, floating_or_tiled = "Floating" },
|
rule = { size = { 500, 500 }, floating_or_tiled = "Floating" },
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
use smithay::wayland::{compositor, shell::xdg::XdgToplevelSurfaceData};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
output::OutputName,
|
output::OutputName,
|
||||||
state::{State, WithState},
|
state::{State, WithState},
|
||||||
|
@ -36,42 +34,8 @@ impl WindowRuleCondition {
|
||||||
WindowRuleCondition::CondAll(conds) => {
|
WindowRuleCondition::CondAll(conds) => {
|
||||||
conds.iter().all(|cond| Self::is_met(cond, state, window))
|
conds.iter().all(|cond| Self::is_met(cond, state, window))
|
||||||
}
|
}
|
||||||
WindowRuleCondition::Class(class) => {
|
WindowRuleCondition::Class(class) => window.class().as_ref() == Some(class),
|
||||||
let Some(wl_surf) = window.wl_surface() else {
|
WindowRuleCondition::Title(title) => window.title().as_ref() == Some(title),
|
||||||
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::Tag(tag) => {
|
WindowRuleCondition::Tag(tag) => {
|
||||||
let Some(tag) = tag.tag(state) else {
|
let Some(tag) = tag.tag(state) else {
|
||||||
tracing::warn!("WindowRuleCondition no tag");
|
tracing::warn!("WindowRuleCondition no tag");
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use smithay::{
|
use smithay::{
|
||||||
delegate_xdg_shell,
|
delegate_xdg_shell,
|
||||||
desktop::{
|
desktop::{
|
||||||
find_popup_root_surface, layer_map_for_output, space::SpaceElement, PopupKeyboardGrab,
|
find_popup_root_surface, layer_map_for_output, PopupKeyboardGrab, PopupKind,
|
||||||
PopupKind, PopupPointerGrab, PopupUngrabStrategy, Window, WindowSurfaceType,
|
PopupPointerGrab, PopupUngrabStrategy, Window, WindowSurfaceType,
|
||||||
},
|
},
|
||||||
input::{pointer::Focus, Seat},
|
input::{pointer::Focus, Seat},
|
||||||
output::Output,
|
output::Output,
|
||||||
|
@ -13,7 +13,7 @@ use smithay::{
|
||||||
Resource,
|
Resource,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
utils::{Point, Rectangle, Serial, SERIAL_COUNTER},
|
utils::{Serial, SERIAL_COUNTER},
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::{self, CompositorHandler},
|
compositor::{self, CompositorHandler},
|
||||||
shell::xdg::{
|
shell::xdg::{
|
||||||
|
@ -24,13 +24,9 @@ use smithay::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::msg::window_rules::{self, WindowRule},
|
|
||||||
focus::FocusTarget,
|
focus::FocusTarget,
|
||||||
state::{State, WithState},
|
state::{State, WithState},
|
||||||
window::{
|
window::{window_state::LocationRequestState, WindowElement, BLOCKER_COUNTER},
|
||||||
window_state::{FloatingOrTiled, LocationRequestState},
|
|
||||||
WindowElement, BLOCKER_COUNTER,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
impl XdgShellHandler for State {
|
impl XdgShellHandler for State {
|
||||||
|
@ -116,110 +112,7 @@ impl XdgShellHandler for State {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|data| {
|
|data| {
|
||||||
for (cond, rule) in data.state.window_rules.iter() {
|
data.state.apply_window_rules(&window);
|
||||||
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))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(focused_output) = data.state.focus_state.focused_output.clone() {
|
if let Some(focused_output) = data.state.focus_state.focused_output.clone() {
|
||||||
data.state.update_windows(&focused_output);
|
data.state.update_windows(&focused_output);
|
||||||
|
|
|
@ -154,6 +154,11 @@ impl XwmHandler for CalloopData {
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
self.state.windows.push(window.clone());
|
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() {
|
if let Some(focused_output) = self.state.focus_state.focused_output.clone() {
|
||||||
self.state.update_windows(&focused_output);
|
self.state.update_windows(&focused_output);
|
||||||
BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst);
|
BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
|
151
src/window.rs
151
src/window.rs
|
@ -25,16 +25,20 @@ use smithay::{
|
||||||
},
|
},
|
||||||
utils::{user_data::UserDataMap, IsAlive, Logical, Point, Rectangle, Serial, Size},
|
utils::{user_data::UserDataMap, IsAlive, Logical, Point, Rectangle, Serial, Size},
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::{Blocker, BlockerState, SurfaceData},
|
compositor::{self, Blocker, BlockerState, SurfaceData},
|
||||||
dmabuf::DmabufFeedback,
|
dmabuf::DmabufFeedback,
|
||||||
seat::WaylandFocus,
|
seat::WaylandFocus,
|
||||||
|
shell::xdg::XdgToplevelSurfaceData,
|
||||||
},
|
},
|
||||||
xwayland::X11Surface,
|
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;
|
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.
|
/// Get the output this window is on.
|
||||||
///
|
///
|
||||||
/// This method gets the first tag the window has and returns its output.
|
/// 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))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue