diff --git a/src/handlers.rs b/src/handlers.rs index 9c90259..0eb1bd9 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -121,6 +121,12 @@ impl CompositorHandler for State { window.with_state(|state| { if let WindowResizeState::Acknowledged(new_pos) = state.resize_state { state.resize_state = WindowResizeState::Idle; + if let WindowElement::X11(surface) = &window { + tracing::debug!("setting x11 win to mapped"); + if !surface.is_override_redirect() { + surface.set_mapped(true).expect("failed to map x11 win"); + } + } self.space.map_element(window.clone(), new_pos, false); } }); @@ -296,7 +302,7 @@ impl XdgShellHandler for State { first_tag.layout().layout( self.windows.clone(), state.focused_tags().cloned().collect(), - &self.space, + self, &focused_output, ); } @@ -350,15 +356,15 @@ impl XdgShellHandler for State { .wl_surface() .is_some_and(|surf| &surf != surface.wl_surface()) }); - if let Some(focused_output) = self.focus_state.focused_output.as_ref() { + if let Some(focused_output) = self.focus_state.focused_output.as_ref().cloned() { focused_output.with_state(|state| { let first_tag = state.focused_tags().next(); if let Some(first_tag) = first_tag { first_tag.layout().layout( self.windows.clone(), state.focused_tags().cloned().collect(), - &self.space, - focused_output, + self, + &focused_output, ); } }); diff --git a/src/handlers/xwayland.rs b/src/handlers/xwayland.rs index 9474935..54bfc80 100644 --- a/src/handlers/xwayland.rs +++ b/src/handlers/xwayland.rs @@ -4,8 +4,10 @@ // // SPDX-License-Identifier: MPL-2.0 +use std::time::Duration; + use smithay::{ - desktop::space::SpaceElement, + desktop::{space::SpaceElement, utils::surface_primary_scanout_output}, input::pointer::Focus, reexports::wayland_server::Resource, utils::{Logical, Rectangle, SERIAL_COUNTER}, @@ -20,7 +22,7 @@ use crate::{ backend::Backend, grab::resize_grab::{ResizeSurfaceGrab, ResizeSurfaceState}, state::{CalloopData, WithState}, - window::{WindowBlocker, WindowElement, BLOCKER_COUNTER}, + window::{WindowBlocker, WindowElement, BLOCKER_COUNTER, window_state::Float}, }; impl XwmHandler for CalloopData { @@ -34,16 +36,15 @@ impl XwmHandler for CalloopData { fn map_window_request(&mut self, xwm: XwmId, window: X11Surface) { tracing::debug!("new x11 window from map_window_request"); + tracing::debug!("window popup is {}", window.is_popup()); + window.set_mapped(true).expect("failed to map x11 window"); let window = WindowElement::X11(window); - // TODO: place the window in the space self.state.space.map_element(window.clone(), (0, 0), true); - let bbox = self - .state - .space - .element_bbox(&window) - .expect("failed to get x11 bbox"); + // let geo = window.geometry(); let WindowElement::X11(surface) = &window else { unreachable!() }; + let bbox = self.state.space.element_bbox(&window).unwrap(); + tracing::debug!("map_window_request, configuring with bbox {bbox:?}"); surface .configure(Some(bbox)) .expect("failed to configure x11 window"); @@ -73,6 +74,21 @@ impl XwmHandler for CalloopData { tracing::debug!("new window, tags are {:?}", state.tags); }); + window.with_state(|state| { + let WindowElement::X11(surface) = &window else { unreachable!() }; + let is_popup = surface.window_type().is_some_and(|typ| !matches!(typ, smithay::xwayland::xwm::WmWindowType::Normal)); + if surface.is_popup() || is_popup || surface.min_size() == surface.max_size() { + state.floating = Float::Floating; + } + }); + + + // self.state.space.map_element(window.clone(), (0, 0), true); + // self.state.space.raise_element(&window, true); + // let WindowElement::X11(surface) = &window else { unreachable!() }; + // self.state.xwm.as_mut().unwrap().raise_window(surface).unwrap(); + + let windows_on_output = self .state .windows @@ -96,7 +112,6 @@ impl XwmHandler for CalloopData { .collect::>(); self.state.windows.push(window.clone()); - // self.space.map_element(window.clone(), (0, 0), true); if let Some(focused_output) = self.state.focus_state.focused_output.clone() { focused_output.with_state(|state| { let first_tag = state.focused_tags().next(); @@ -104,7 +119,7 @@ impl XwmHandler for CalloopData { first_tag.layout().layout( self.state.windows.clone(), state.focused_tags().cloned().collect(), - &self.state.space, + &mut self.state, &focused_output, ); } @@ -120,8 +135,8 @@ impl XwmHandler for CalloopData { } } let clone = window.clone(); - self.state.loop_handle.insert_idle(|data| { - crate::state::schedule_on_commit(data, vec![clone], move |data| { + self.state.loop_handle.insert_idle(move |data| { + crate::state::schedule_on_commit(data, vec![clone.clone()], move |data| { BLOCKER_COUNTER.store(0, std::sync::atomic::Ordering::SeqCst); tracing::debug!( "blocker {}", @@ -135,6 +150,20 @@ impl XwmHandler for CalloopData { .client_compositor_state(&client) .blocker_cleared(&mut data.state, &data.display.handle()) } + + + // data.state.loop_handle.insert_idle(move |dt| { + // + // let WindowElement::X11(surface) = &clone else { unreachable!() }; + // let is_popup = surface.window_type().is_some_and(|typ| !matches!(typ, smithay::xwayland::xwm::WmWindowType::Normal)); + // if surface.is_popup() || is_popup || surface.min_size() == surface.max_size() { + // if let Some(xwm) = dt.state.xwm.as_mut() { + // tracing::debug!("raising x11 modal"); + // xwm.raise_window(surface).expect("failed to raise x11 win"); + // dt.state.space.raise_element(&clone, true); + // } + // } + // }); }) }); } @@ -152,28 +181,36 @@ impl XwmHandler for CalloopData { } } - fn mapped_override_redirect_window(&mut self, xwm: XwmId, window: X11Surface) { + // fn map_window_notify(&mut self, xwm: XwmId, window: X11Surface) { + // // + // } + + fn mapped_override_redirect_window(&mut self, _xwm: XwmId, window: X11Surface) { let loc = window.geometry().loc; let window = WindowElement::X11(window); + tracing::debug!("mapped_override_redirect_window to loc {loc:?}"); self.state.space.map_element(window, loc, true); } - fn unmapped_window(&mut self, xwm: XwmId, window: X11Surface) { + fn unmapped_window(&mut self, _xwm: XwmId, window: X11Surface) { tracing::debug!("unmapped x11 window"); let win = self .state .space .elements() - .find(|elem| matches!(elem, WindowElement::X11(surface) if surface == &window)) + .find(|elem| { + matches!(elem, + WindowElement::X11(surface) if surface == &window) + }) .cloned(); if let Some(win) = win { self.state.space.unmap_elem(&win); // self.state.windows.retain(|elem| &win != elem); - if win.with_state(|state| state.floating.is_tiled()) { - if let Some(output) = win.output(&self.state) { - self.state.re_layout(&output); - } - } + // if win.with_state(|state| state.floating.is_tiled()) { + // if let Some(output) = win.output(&self.state) { + // self.state.re_layout(&output); + // } + // } } if !window.is_override_redirect() { window.set_mapped(false).expect("failed to unmap x11 win"); @@ -232,6 +269,7 @@ impl XwmHandler for CalloopData { geometry: Rectangle, above: Option, ) { + tracing::debug!("x11 configure_notify"); let Some(win) = self .state .space @@ -241,7 +279,11 @@ impl XwmHandler for CalloopData { else { return; }; + tracing::debug!("geo: {geometry:?}"); self.state.space.map_element(win, geometry.loc, false); + // for output in self.state.space.outputs_for_element(&win) { + // win.send_frame(&output, self.state.clock.now(), Some(Duration::ZERO), surface_primary_scanout_output); + // } // TODO: anvil has a TODO here } @@ -249,13 +291,16 @@ impl XwmHandler for CalloopData { // TODO: unmaximize request - // TODO: fullscreen request + fn fullscreen_request(&mut self, xwm: XwmId, window: X11Surface) { + // TODO: + window.set_fullscreen(true).unwrap(); + } // TODO: unfullscreen request fn resize_request( &mut self, - xwm: XwmId, + _xwm: XwmId, window: X11Surface, button: u32, resize_edge: smithay::xwayland::xwm::ResizeEdge, @@ -296,7 +341,7 @@ impl XwmHandler for CalloopData { win.clone(), resize_edge.into(), Rectangle::from_loc_and_size(initial_window_location, initial_window_size), - 0x110, // BUTTON_LEFT + button, // BUTTON_LEFT ); if let Some(grab) = grab { diff --git a/src/layout.rs b/src/layout.rs index 3221053..7c177dc 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -2,7 +2,6 @@ use itertools::{Either, Itertools}; use smithay::{ - desktop::Space, output::Output, utils::{Logical, Size}, }; @@ -27,35 +26,30 @@ pub enum Layout { } impl Layout { - pub fn layout( + pub fn layout( &self, windows: Vec, tags: Vec, - space: &Space, + state: &mut State, output: &Output, ) { let windows = filter_windows(&windows, tags); - let Some(output_geo) = space.output_geometry(output) else { - tracing::error!("could not get output geometry"); - return; - }; - - let output_loc = output.current_location(); - match self { - Layout::MasterStack => master_stack(windows, space, output), - Layout::Dwindle => dwindle(windows, space, output), - Layout::Spiral => spiral(windows, space, output), + Layout::MasterStack => master_stack(windows, state, output), + Layout::Dwindle => dwindle(windows, state, output), + Layout::Spiral => spiral(windows, state, output), layout @ (Layout::CornerTopLeft | Layout::CornerTopRight | Layout::CornerBottomLeft - | Layout::CornerBottomRight) => corner(layout, windows, space, output), + | Layout::CornerBottomRight) => corner(layout, windows, state, output), } } } -fn master_stack(windows: Vec, space: &Space, output: &Output) { +fn master_stack(windows: Vec, state: &mut State, output: &Output) { + let space = &mut state.space; + let Some(output_geo) = space.output_geometry(output) else { tracing::error!("could not get output geometry"); return; @@ -72,11 +66,11 @@ fn master_stack(windows: Vec, space: &Space, outpu if stack_count == 0 { // one window - master.request_size_change(output_loc, output_geo.size); + master.request_size_change(state, output_loc, output_geo.size); } else { let loc = (output_loc.x, output_loc.y).into(); let new_master_size: Size = (output_geo.size.w / 2, output_geo.size.h).into(); - master.request_size_change(loc, new_master_size); + master.request_size_change(state, loc, new_master_size); let stack_count = stack_count; @@ -93,6 +87,7 @@ fn master_stack(windows: Vec, space: &Space, outpu for (i, win) in stack.enumerate() { win.request_size_change( + state, (output_geo.size.w / 2 + output_loc.x, y_s[i] + output_loc.y).into(), (output_geo.size.w / 2, i32::max(heights[i], 40)).into(), ); @@ -100,7 +95,9 @@ fn master_stack(windows: Vec, space: &Space, outpu } } -fn dwindle(windows: Vec, space: &Space, output: &Output) { +fn dwindle(windows: Vec, state: &mut State, output: &Output) { + let space = &state.space; + let Some(output_geo) = space.output_geometry(output) else { tracing::error!("could not get output geometry"); return; @@ -112,7 +109,7 @@ fn dwindle(windows: Vec, space: &Space, output: &O if iter.peek().is_none() { if let Some(window) = windows.first() { - window.request_size_change(output_loc, output_geo.size); + window.request_size_change(state, output_loc, output_geo.size); } } else { let mut win1_size = output_geo.size; @@ -137,6 +134,7 @@ fn dwindle(windows: Vec, space: &Space, output: &O let width_partition = win1_size.w / 2; win1.request_size_change( + state, win1_loc, (win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(), ); @@ -144,12 +142,13 @@ fn dwindle(windows: Vec, space: &Space, output: &O win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into(); win1_size = (width_partition, i32::max(win1_size.h, 40)).into(); - win2.request_size_change(win1_loc, win1_size); + win2.request_size_change(state, win1_loc, win1_size); } Slice::Below => { let height_partition = win1_size.h / 2; win1.request_size_change( + state, win1_loc, (win1_size.w, i32::max(win1_size.h - height_partition, 40)).into(), ); @@ -157,14 +156,15 @@ fn dwindle(windows: Vec, space: &Space, output: &O win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into(); win1_size = (win1_size.w, i32::max(height_partition, 40)).into(); - win2.request_size_change(win1_loc, win1_size); + win2.request_size_change(state, win1_loc, win1_size); } } } } } -fn spiral(windows: Vec, space: &Space, output: &Output) { +fn spiral(windows: Vec, state: &mut State, output: &Output) { + let space = &state.space; let Some(output_geo) = space.output_geometry(output) else { tracing::error!("could not get output geometry"); return; @@ -176,7 +176,7 @@ fn spiral(windows: Vec, space: &Space, output: &Ou if iter.peek().is_none() { if let Some(window) = windows.first() { - window.request_size_change(output_loc, output_geo.size); + window.request_size_change(state, output_loc, output_geo.size); } } else { let mut win1_loc = output_loc; @@ -206,59 +206,64 @@ fn spiral(windows: Vec, space: &Space, output: &Ou let height_partition = win1_size.h / 2; win1.request_size_change( + state, (win1_loc.x, win1_loc.y + height_partition).into(), (win1_size.w, i32::max(win1_size.h - height_partition, 40)).into(), ); win1_size = (win1_size.w, i32::max(height_partition, 40)).into(); - win2.request_size_change(win1_loc, win1_size); + win2.request_size_change(state, win1_loc, win1_size); } Slice::Below => { let height_partition = win1_size.h / 2; win1.request_size_change( + state, win1_loc, (win1_size.w, win1_size.h - i32::max(height_partition, 40)).into(), ); win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into(); win1_size = (win1_size.w, i32::max(height_partition, 40)).into(); - win2.request_size_change(win1_loc, win1_size); + win2.request_size_change(state, win1_loc, win1_size); } Slice::Left => { let width_partition = win1_size.w / 2; win1.request_size_change( + state, (win1_loc.x + width_partition, win1_loc.y).into(), (win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(), ); win1_size = (width_partition, i32::max(win1_size.h, 40)).into(); - win2.request_size_change(win1_loc, win1_size); + win2.request_size_change(state, win1_loc, win1_size); } Slice::Right => { let width_partition = win1_size.w / 2; win1.request_size_change( + state, win1_loc, (win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(), ); win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into(); win1_size = (width_partition, i32::max(win1_size.h, 40)).into(); - win2.request_size_change(win1_loc, win1_size); + win2.request_size_change(state, win1_loc, win1_size); } } } } } -fn corner( +fn corner( layout: &Layout, windows: Vec, - space: &Space, + state: &mut State, output: &Output, ) { + let space = &state.space; let Some(output_geo) = space.output_geometry(output) else { tracing::error!("could not get output geometry"); return; @@ -268,15 +273,17 @@ fn corner( match windows.len() { 0 => (), 1 => { - windows[0].request_size_change(output_loc, output_geo.size); + windows[0].request_size_change(state, output_loc, output_geo.size); } 2 => { windows[0].request_size_change( + state, output_loc, (output_geo.size.w / 2, output_geo.size.h).into(), ); windows[1].request_size_change( + state, (output_loc.x + output_geo.size.w / 2, output_loc.y).into(), (output_geo.size.w / 2, output_geo.size.h).into(), ); @@ -296,6 +303,7 @@ fn corner( let div_factor = 2; corner.request_size_change( + state, match layout { Layout::CornerTopLeft => (output_loc.x, output_loc.y), Layout::CornerTopRight => ( @@ -335,6 +343,7 @@ fn corner( for (i, win) in vert_stack.iter().enumerate() { win.request_size_change( + state, ( match layout { Layout::CornerTopLeft | Layout::CornerBottomLeft => { @@ -367,6 +376,7 @@ fn corner( for (i, win) in horiz_stack.iter().enumerate() { win.request_size_change( + state, match layout { Layout::CornerTopLeft => { (x_s[i] + output_loc.x, output_loc.y + output_geo.size.h / 2) @@ -392,16 +402,15 @@ fn corner( fn filter_windows(windows: &[WindowElement], tags: Vec) -> Vec { windows .iter() + .filter(|window| window.with_state(|state| state.floating.is_tiled())) .filter(|window| { window.with_state(|state| { - state.floating.is_tiled() && { - for tag in state.tags.iter() { - if tags.iter().any(|tg| tg == tag) { - return true; - } + for tag in state.tags.iter() { + if tags.iter().any(|tg| tg == tag) { + return true; } - false } + false }) }) .cloned() diff --git a/src/main.rs b/src/main.rs index 62bd083..24de813 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ //! While Pinnacle is not a library, this documentation serves to guide those who want to //! contribute or learn how building something like this works. -#![deny(unused_imports)] // gonna force myself to keep stuff clean +// #![deny(unused_imports)] // gonna force myself to keep stuff clean #![warn(clippy::unwrap_used)] mod api; diff --git a/src/state.rs b/src/state.rs index 33aaa1f..fd934f0 100644 --- a/src/state.rs +++ b/src/state.rs @@ -159,7 +159,7 @@ impl State { .element_location(&window) .unwrap_or((0, 0).into()); let window_size = window.geometry().size; - window.request_size_change(window_loc, window_size); + window.request_size_change(self, window_loc, window_size); } Msg::MoveWindowToTag { window_id, tag_id } => { let Some(window) = window_id.window(self) else { return }; @@ -623,11 +623,6 @@ impl State { .any(|tag| tag.output(self).is_some_and(|op| &op == output)) }) }) - .filter(|win| match win { - WindowElement::Wayland(win) => !win.with_state(|state| state.minimized), - WindowElement::X11(surf) => !surf.is_minimized(), - }) - .filter(|win| win.alive()) .cloned() .collect::>(); let (render, do_not_render) = output.with_state(|state| { @@ -636,7 +631,7 @@ impl State { first_tag.layout().layout( self.windows.clone(), state.focused_tags().cloned().collect(), - &self.space, + self, output, ); } @@ -656,11 +651,22 @@ impl State { }) }); + tracing::debug!( + "{} to render, {} to not render", + render.len(), + do_not_render.len() + ); + let clone = render.clone(); self.loop_handle.insert_idle(|data| { schedule_on_commit(data, clone, |dt| { for win in do_not_render { dt.state.space.unmap_elem(&win); + if let WindowElement::X11(surface) = win { + if !surface.is_override_redirect() { + surface.set_mapped(false).expect("failed to unmap x11 win"); + } + } } }) }); diff --git a/src/window.rs b/src/window.rs index a8dc34a..0cd7c08 100644 --- a/src/window.rs +++ b/src/window.rs @@ -181,7 +181,12 @@ impl WindowElement { } /// Request a size and loc change. - pub fn request_size_change(&self, new_loc: Point, new_size: Size) { + pub fn request_size_change( + &self, + state: &mut State, + new_loc: Point, + new_size: Size, + ) { match self { WindowElement::Wayland(window) => { window.toplevel().with_pending_state(|state| { @@ -193,12 +198,25 @@ impl WindowElement { }); } WindowElement::X11(surface) => { + tracing::debug!("sending size change to x11 win"); surface .configure(Rectangle::from_loc_and_size(new_loc, new_size)) .expect("failed to configure x11 win"); - self.with_state(|state| { - state.resize_state = WindowResizeState::Acknowledged(new_loc); - }); + // self.with_state(|state| { + // state.resize_state = WindowResizeState::Acknowledged(new_loc); + // }); + if !surface.is_override_redirect() { + surface.set_mapped(true).unwrap(); + } + state.space.map_element(self.clone(), new_loc, false); + // if let Some(focused_output) = state.focus_state.focused_output.clone() { + // self.send_frame( + // &focused_output, + // state.clock.now(), + // Some(Duration::ZERO), + // surface_primary_scanout_output, + // ); + // } } } } @@ -209,6 +227,22 @@ impl WindowElement { pub fn output(&self, state: &State) -> Option { self.with_state(|st| st.tags.first().and_then(|tag| tag.output(state))) } + + /// Returns `true` if the window element is [`Wayland`]. + /// + /// [`Wayland`]: WindowElement::Wayland + #[must_use] + pub fn is_wayland(&self) -> bool { + matches!(self, Self::Wayland(..)) + } + + /// Returns `true` if the window element is [`X11`]. + /// + /// [`X11`]: WindowElement::X11 + #[must_use] + pub fn is_x11(&self) -> bool { + matches!(self, Self::X11(..)) + } } impl IsAlive for WindowElement { @@ -483,7 +517,7 @@ pub fn toggle_floating(state: &mut State, window: &WindowElement) }); if let Some((prev_loc, prev_size)) = resize { - window.request_size_change(prev_loc, prev_size); + window.request_size_change(state, prev_loc, prev_size); } let output = state.focus_state.focused_output.clone().unwrap();