pinnacle/src/window.rs

236 lines
7.1 KiB
Rust
Raw Normal View History

2023-08-01 11:06:35 -05:00
// SPDX-License-Identifier: GPL-3.0-or-later
2023-06-25 17:18:50 -05:00
2023-12-09 21:11:29 -06:00
pub mod rules;
use std::{cell::RefCell, ops::Deref};
2023-06-18 19:30:52 -05:00
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},
2023-06-18 19:30:52 -05:00
};
2023-05-28 19:14:30 -05:00
2023-12-09 21:11:29 -06:00
use crate::state::{State, WithState};
2023-05-28 19:14:30 -05:00
use self::window_state::WindowElementState;
2023-05-28 19:14:30 -05:00
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)
}
2023-07-24 18:59:05 -05:00
2023-08-11 10:08:38 -05:00
/// Send a geometry change without mapping windows or sending
/// configures to Wayland windows.
///
/// Xwayland windows will still receive a configure.
///
2023-12-16 21:20:29 -06:00
/// RefCell Safety: This method uses a [`RefCell`] on this window.
2023-08-11 10:08:38 -05:00
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);
});
2023-08-11 10:08:38 -05:00
}
WindowSurface::X11(surface) => {
if !surface.is_override_redirect() {
surface
.configure(new_geo)
.expect("failed to configure x11 win");
}
2023-08-11 10:08:38 -05:00
}
}
2024-03-04 19:16:10 -06:00
self.with_state_mut(|state| {
state.target_loc = Some(new_geo.loc);
2023-08-11 10:08:38 -05:00
});
}
2024-03-04 19:16:10 -06:00
/// Get this window's class (app id in Wayland but hey old habits die hard).
2023-09-07 18:00:58 -05:00
pub fn class(&self) -> Option<String> {
match self.0.underlying_surface() {
WindowSurface::Wayland(toplevel) => {
compositor::with_states(toplevel.wl_surface(), |states| {
2023-09-07 18:00:58 -05:00
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()),
2023-09-07 18:00:58 -05:00
}
}
2024-03-04 19:16:10 -06:00
/// Get this window's title.
2023-09-07 18:00:58 -05:00
pub fn title(&self) -> Option<String> {
match self.0.underlying_surface() {
WindowSurface::Wayland(toplevel) => {
compositor::with_states(toplevel.wl_surface(), |states| {
2023-09-07 18:00:58 -05:00
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()),
2023-09-07 18:00:58 -05:00
}
}
/// Get the output this window is on.
///
/// This method gets the first tag the window has and returns its output.
2023-09-15 02:50:42 -05:00
///
2023-12-16 21:20:29 -06:00
/// RefCell Safety: This method uses a [`RefCell`] on this window and every mapped output.
2023-08-28 22:53:24 -05:00
pub fn output(&self, state: &State) -> Option<Output> {
self.with_state(|st| st.tags.first().and_then(|tag| tag.output(state)))
}
2023-12-16 21:20:29 -06:00
/// Returns whether or not this window has an active tag.
///
2024-03-04 19:16:10 -06:00
/// 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()))
2023-09-02 16:51:39 -05:00
}
2023-12-14 14:42:55 -06:00
/// Place this window on the given output, giving it the output's focused tags.
///
2024-03-04 19:16:10 -06:00
/// RefCell Safety: Uses `with_state_mut` on the window and `with_state` on the output
2023-12-14 14:42:55 -06:00
pub fn place_on_output(&self, output: &Output) {
2024-03-04 19:16:10 -06:00
self.with_state_mut(|state| {
2023-12-14 14:42:55 -06:00
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;
2023-09-20 15:13:03 -05:00
fn with_state<F, T>(&self, func: F) -> T
2024-03-04 19:16:10 -06:00
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
2023-09-20 15:13:03 -05:00
F: FnOnce(&mut Self::State) -> T,
{
let state = self
.user_data()
2023-12-16 21:20:29 -06:00
.get_or_insert(|| RefCell::new(WindowElementState::new()));
func(&mut state.borrow_mut())
}
}
2023-08-28 22:53:24 -05:00
impl State {
/// Returns the [Window] associated with a given [WlSurface].
2023-07-24 18:59:05 -05:00
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))
2023-06-30 21:34:07 -05:00
.or_else(|| {
self.windows
.iter()
2023-07-24 18:59:05 -05:00
.find(|&win| win.wl_surface().is_some_and(|surf| &surf == surface))
2023-06-30 21:34:07 -05:00
})
.cloned()
}
2024-03-04 19:16:10 -06:00
/// `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()
}
}