mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-26 19:58:01 +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 } },
|
||||
}, {
|
||||
cond = {
|
||||
cond_all = { { class = "Alacritty" }, { tag = "4" } },
|
||||
cond_all = { { class = "XTerm" }, { tag = "4" } },
|
||||
},
|
||||
rule = { size = { 500, 500 }, floating_or_tiled = "Floating" },
|
||||
})
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
151
src/window.rs
151
src/window.rs
|
@ -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))
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue