mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-02-11 08:48:22 +01:00
76fc825b51
And properly handle input and layering this time
235 lines
7.1 KiB
Rust
235 lines
7.1 KiB
Rust
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
pub mod rules;
|
|
|
|
use std::{cell::RefCell, ops::Deref};
|
|
|
|
use smithay::{
|
|
desktop::{space::SpaceElement, Window, WindowSurface},
|
|
output::Output,
|
|
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
|
utils::{IsAlive, Logical, Point, Rectangle},
|
|
wayland::{compositor, seat::WaylandFocus, shell::xdg::XdgToplevelSurfaceData},
|
|
};
|
|
|
|
use crate::state::{State, WithState};
|
|
|
|
use self::window_state::WindowElementState;
|
|
|
|
pub mod window_state;
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub struct WindowElement(Window);
|
|
|
|
impl Deref for WindowElement {
|
|
type Target = Window;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl WindowElement {
|
|
pub fn new(window: Window) -> Self {
|
|
Self(window)
|
|
}
|
|
|
|
/// Send a geometry change without mapping windows or sending
|
|
/// configures to Wayland windows.
|
|
///
|
|
/// Xwayland windows will still receive a configure.
|
|
///
|
|
/// RefCell Safety: This method uses a [`RefCell`] on this window.
|
|
pub fn change_geometry(&self, new_geo: Rectangle<i32, Logical>) {
|
|
match self.0.underlying_surface() {
|
|
WindowSurface::Wayland(toplevel) => {
|
|
toplevel.with_pending_state(|state| {
|
|
state.size = Some(new_geo.size);
|
|
});
|
|
}
|
|
WindowSurface::X11(surface) => {
|
|
if !surface.is_override_redirect() {
|
|
surface
|
|
.configure(new_geo)
|
|
.expect("failed to configure x11 win");
|
|
}
|
|
}
|
|
}
|
|
self.with_state_mut(|state| {
|
|
state.target_loc = Some(new_geo.loc);
|
|
});
|
|
}
|
|
|
|
/// Get this window's class (app id in Wayland but hey old habits die hard).
|
|
pub fn class(&self) -> Option<String> {
|
|
match self.0.underlying_surface() {
|
|
WindowSurface::Wayland(toplevel) => {
|
|
compositor::with_states(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()
|
|
})
|
|
}
|
|
WindowSurface::X11(surface) => Some(surface.class()),
|
|
}
|
|
}
|
|
|
|
/// Get this window's title.
|
|
pub fn title(&self) -> Option<String> {
|
|
match self.0.underlying_surface() {
|
|
WindowSurface::Wayland(toplevel) => {
|
|
compositor::with_states(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()
|
|
})
|
|
}
|
|
WindowSurface::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.
|
|
///
|
|
/// RefCell Safety: This method uses a [`RefCell`] on this window and every mapped output.
|
|
pub fn output(&self, state: &State) -> Option<Output> {
|
|
self.with_state(|st| st.tags.first().and_then(|tag| tag.output(state)))
|
|
}
|
|
|
|
/// Returns whether or not this window has an active tag.
|
|
///
|
|
/// RefCell Safety: This calls `with_state` on `self`.
|
|
pub fn is_on_active_tag(&self) -> bool {
|
|
self.with_state(|state| state.tags.iter().any(|tag| tag.active()))
|
|
}
|
|
|
|
/// Place this window on the given output, giving it the output's focused tags.
|
|
///
|
|
/// RefCell Safety: Uses `with_state_mut` on the window and `with_state` on the output
|
|
pub fn place_on_output(&self, output: &Output) {
|
|
self.with_state_mut(|state| {
|
|
state.tags = output.with_state(|state| {
|
|
let output_tags = state.focused_tags().cloned().collect::<Vec<_>>();
|
|
if !output_tags.is_empty() {
|
|
output_tags
|
|
} else if let Some(first_tag) = state.tags.first() {
|
|
vec![first_tag.clone()]
|
|
} else {
|
|
vec![]
|
|
}
|
|
});
|
|
|
|
tracing::debug!(
|
|
"Placed window on {} with tags {:?}",
|
|
output.name(),
|
|
state.tags
|
|
);
|
|
});
|
|
}
|
|
|
|
pub fn is_x11_override_redirect(&self) -> bool {
|
|
matches!(self.x11_surface(), Some(surface) if surface.is_override_redirect())
|
|
}
|
|
}
|
|
|
|
impl SpaceElement for WindowElement {
|
|
fn bbox(&self) -> Rectangle<i32, Logical> {
|
|
self.0.bbox()
|
|
}
|
|
|
|
fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool {
|
|
self.0.is_in_input_region(point)
|
|
}
|
|
|
|
fn set_activate(&self, activated: bool) {
|
|
self.0.set_activate(activated)
|
|
}
|
|
|
|
fn output_enter(&self, output: &Output, overlap: Rectangle<i32, Logical>) {
|
|
self.0.output_enter(output, overlap)
|
|
}
|
|
|
|
fn output_leave(&self, output: &Output) {
|
|
self.0.output_leave(output)
|
|
}
|
|
|
|
fn geometry(&self) -> Rectangle<i32, Logical> {
|
|
self.0.geometry()
|
|
}
|
|
|
|
fn z_index(&self) -> u8 {
|
|
self.0.z_index()
|
|
}
|
|
|
|
fn refresh(&self) {
|
|
self.0.refresh();
|
|
}
|
|
}
|
|
|
|
impl IsAlive for WindowElement {
|
|
fn alive(&self) -> bool {
|
|
self.0.alive()
|
|
}
|
|
}
|
|
|
|
impl WithState for WindowElement {
|
|
type State = WindowElementState;
|
|
|
|
fn with_state<F, T>(&self, func: F) -> T
|
|
where
|
|
F: FnOnce(&Self::State) -> T,
|
|
{
|
|
let state = self
|
|
.user_data()
|
|
.get_or_insert(|| RefCell::new(WindowElementState::new()));
|
|
|
|
func(&state.borrow())
|
|
}
|
|
|
|
fn with_state_mut<F, T>(&self, func: F) -> T
|
|
where
|
|
F: FnOnce(&mut Self::State) -> T,
|
|
{
|
|
let state = self
|
|
.user_data()
|
|
.get_or_insert(|| RefCell::new(WindowElementState::new()));
|
|
|
|
func(&mut state.borrow_mut())
|
|
}
|
|
}
|
|
|
|
impl State {
|
|
/// Returns the [Window] associated with a given [WlSurface].
|
|
pub fn window_for_surface(&self, surface: &WlSurface) -> Option<WindowElement> {
|
|
self.space
|
|
.elements()
|
|
.find(|window| window.wl_surface().map(|s| s == *surface).unwrap_or(false))
|
|
.or_else(|| {
|
|
self.windows
|
|
.iter()
|
|
.find(|&win| win.wl_surface().is_some_and(|surf| &surf == surface))
|
|
})
|
|
.cloned()
|
|
}
|
|
|
|
/// `window_for_surface` but for windows that haven't commited a buffer yet.
|
|
///
|
|
/// Currently only used in `ensure_initial_configure` in [`handlers`][crate::handlers].
|
|
pub fn new_window_for_surface(&self, surface: &WlSurface) -> Option<WindowElement> {
|
|
self.new_windows
|
|
.iter()
|
|
.find(|&win| win.wl_surface().is_some_and(|surf| &surf == surface))
|
|
.cloned()
|
|
}
|
|
}
|