diff --git a/src/api.rs b/src/api.rs index 5e4d568..c6484dd 100644 --- a/src/api.rs +++ b/src/api.rs @@ -738,6 +738,14 @@ impl tag_service_server::TagService for TagService { return; }; + let Some(output) = tag.output(&state.pinnacle) else { + return; + }; + + let snapshots = state.backend.with_renderer(|renderer| { + capture_snapshots_on_output(&mut state.pinnacle, renderer, &output) + }); + match set_or_toggle { SetOrToggle::Set => tag.set_active(true, state), SetOrToggle::Unset => tag.set_active(false, state), @@ -745,12 +753,12 @@ impl tag_service_server::TagService for TagService { SetOrToggle::Unspecified => unreachable!(), } - let Some(output) = tag.output(&state.pinnacle) else { - return; - }; - state.pinnacle.fixup_xwayland_window_layering(); + output.with_state_mut(|op_state| { + op_state.new_wait_layout_transaction(state.pinnacle.loop_handle.clone(), snapshots) + }); + state.pinnacle.request_layout(&output); state.update_keyboard_focus(&output); state.schedule_render(&output); @@ -773,6 +781,10 @@ impl tag_service_server::TagService for TagService { return; }; + let snapshots = state.backend.with_renderer(|renderer| { + capture_snapshots_on_output(&mut state.pinnacle, renderer, &output) + }); + output.with_state(|op_state| { for op_tag in op_state.tags.iter() { op_tag.set_active(false, state); @@ -782,6 +794,10 @@ impl tag_service_server::TagService for TagService { state.pinnacle.fixup_xwayland_window_layering(); + output.with_state_mut(|op_state| { + op_state.new_wait_layout_transaction(state.pinnacle.loop_handle.clone(), snapshots) + }); + state.pinnacle.request_layout(&output); state.update_keyboard_focus(&output); state.schedule_render(&output); diff --git a/src/api/window.rs b/src/api/window.rs index d165db0..7cd608d 100644 --- a/src/api/window.rs +++ b/src/api/window.rs @@ -21,7 +21,10 @@ use smithay::{ use tonic::{Request, Response, Status}; use tracing::warn; -use crate::{output::OutputName, state::WithState, tag::TagId, window::window_state::WindowId}; +use crate::{ + output::OutputName, render::util::snapshot::capture_snapshots_on_output, state::WithState, + tag::TagId, window::window_state::WindowId, +}; use super::{run_unary, run_unary_no_response, StateFnSender}; @@ -136,6 +139,12 @@ impl window_service_server::WindowService for WindowService { return; }; + let snapshots = window.output(pinnacle).map(|output| { + state.backend.with_renderer(|renderer| { + capture_snapshots_on_output(pinnacle, renderer, &output) + }) + }); + match set_or_toggle { SetOrToggle::Set => { if !window.with_state(|state| state.fullscreen_or_maximized.is_fullscreen()) { @@ -155,6 +164,12 @@ impl window_service_server::WindowService for WindowService { return; }; + if let Some(snapshots) = snapshots { + output.with_state_mut(|op_state| { + op_state.new_wait_layout_transaction(pinnacle.loop_handle.clone(), snapshots) + }); + } + pinnacle.request_layout(&output); state.schedule_render(&output); }) @@ -185,6 +200,12 @@ impl window_service_server::WindowService for WindowService { return; }; + let snapshots = window.output(pinnacle).map(|output| { + state.backend.with_renderer(|renderer| { + capture_snapshots_on_output(pinnacle, renderer, &output) + }) + }); + match set_or_toggle { SetOrToggle::Set => { if !window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) { @@ -204,6 +225,12 @@ impl window_service_server::WindowService for WindowService { return; }; + if let Some(snapshots) = snapshots { + output.with_state_mut(|op_state| { + op_state.new_wait_layout_transaction(pinnacle.loop_handle.clone(), snapshots) + }); + } + pinnacle.request_layout(&output); state.schedule_render(&output); }) @@ -234,6 +261,12 @@ impl window_service_server::WindowService for WindowService { return; }; + let snapshots = window.output(pinnacle).map(|output| { + state.backend.with_renderer(|renderer| { + capture_snapshots_on_output(pinnacle, renderer, &output) + }) + }); + match set_or_toggle { SetOrToggle::Set => { if !window.with_state(|state| state.floating_or_tiled.is_floating()) { @@ -253,6 +286,12 @@ impl window_service_server::WindowService for WindowService { return; }; + if let Some(snapshots) = snapshots { + output.with_state_mut(|op_state| { + op_state.new_wait_layout_transaction(pinnacle.loop_handle.clone(), snapshots) + }); + } + pinnacle.request_layout(&output); state.schedule_render(&output); }) @@ -363,12 +402,24 @@ impl window_service_server::WindowService for WindowService { let Some(tag) = tag_id.tag(pinnacle) else { return }; + let snapshots = window.output(pinnacle).map(|output| { + state.backend.with_renderer(|renderer| { + capture_snapshots_on_output(pinnacle, renderer, &output) + }) + }); + window.with_state_mut(|state| { state.tags = vec![tag.clone()]; }); let Some(output) = tag.output(pinnacle) else { return }; + if let Some(snapshots) = snapshots { + output.with_state_mut(|op_state| { + op_state.new_wait_layout_transaction(pinnacle.loop_handle.clone(), snapshots) + }); + } + pinnacle.request_layout(&output); state.schedule_render(&output); @@ -405,6 +456,12 @@ impl window_service_server::WindowService for WindowService { }; let Some(tag) = tag_id.tag(pinnacle) else { return }; + let snapshots = window.output(pinnacle).map(|output| { + state.backend.with_renderer(|renderer| { + capture_snapshots_on_output(pinnacle, renderer, &output) + }) + }); + // TODO: turn state.tags into a hashset match set_or_toggle { SetOrToggle::Set => window.with_state_mut(|state| { @@ -425,6 +482,13 @@ impl window_service_server::WindowService for WindowService { } let Some(output) = tag.output(pinnacle) else { return }; + + if let Some(snapshots) = snapshots { + output.with_state_mut(|op_state| { + op_state.new_wait_layout_transaction(pinnacle.loop_handle.clone(), snapshots) + }); + } + pinnacle.request_layout(&output); state.schedule_render(&output); diff --git a/src/focus.rs b/src/focus.rs index c623fe5..4edb3dc 100644 --- a/src/focus.rs +++ b/src/focus.rs @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later use smithay::{desktop::space::SpaceElement, output::Output, utils::SERIAL_COUNTER}; -use tracing::warn; use crate::{ state::{Pinnacle, State, WithState}, @@ -77,14 +76,7 @@ impl Pinnacle { } /// Raise a window to the top of the z-index stack. - /// - /// This does nothing if the window is unmapped. pub fn raise_window(&mut self, window: WindowElement, activate: bool) { - if self.space.elements().all(|win| win != window) { - warn!("Tried to raise an unmapped window"); - return; - } - self.space.raise_element(&window, activate); self.z_index_stack.retain(|win| win != window); diff --git a/src/handlers.rs b/src/handlers.rs index 142ef2e..d1c76dc 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pub mod session_lock; +pub mod window; mod xdg_shell; mod xwayland; @@ -32,7 +33,7 @@ use smithay::{ Client, Resource, }, }, - utils::{Logical, Point, Rectangle, SERIAL_COUNTER}, + utils::{Logical, Point, Rectangle}, wayland::{ buffer::BufferHandler, compositor::{ @@ -202,6 +203,8 @@ impl CompositorHandler for State { .retain(|win| win != unmapped_window); self.pinnacle.windows.push(unmapped_window.clone()); + self.pinnacle.raise_window(unmapped_window.clone(), true); + self.pinnacle.apply_window_rules(&unmapped_window); if let Some(focused_output) = self.pinnacle.focused_output().cloned() { diff --git a/src/handlers/window.rs b/src/handlers/window.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/handlers/window.rs @@ -0,0 +1 @@ + diff --git a/src/handlers/xwayland.rs b/src/handlers/xwayland.rs index 221efd8..1fe30c1 100644 --- a/src/handlers/xwayland.rs +++ b/src/handlers/xwayland.rs @@ -26,6 +26,7 @@ use tracing::{debug, error, trace, warn}; use crate::{ cursor::Cursor, focus::keyboard::KeyboardFocusTarget, + render::util::snapshot::capture_snapshots_on_output, state::{Pinnacle, State, WithState}, window::{window_state::FloatingOrTiled, WindowElement}, }; @@ -104,7 +105,8 @@ impl XwmHandler for State { }); } - // TODO: will an unmap -> map duplicate the window + // TODO: do snapshot and transaction here BUT ONLY IF TILED AND ON ACTIVE TAG + self.pinnacle.windows.push(window.clone()); self.pinnacle.raise_window(window.clone(), true); @@ -432,13 +434,27 @@ impl State { .iter() .find(|elem| elem.x11_surface() == Some(&surface)) .cloned(); - if let Some(win) = win { debug!("removing x11 window from windows"); + let snapshots = win.output(&self.pinnacle).map(|output| { + self.backend.with_renderer(|renderer| { + capture_snapshots_on_output(&mut self.pinnacle, renderer, &output) + }) + }); + self.pinnacle.remove_window(&win, false); if let Some(output) = win.output(&self.pinnacle) { + if let Some(snapshots) = snapshots { + output.with_state_mut(|state| { + state.new_wait_layout_transaction( + self.pinnacle.loop_handle.clone(), + snapshots, + ) + }); + } + self.pinnacle.request_layout(&output); self.update_keyboard_focus(&output); self.schedule_render(&output); diff --git a/src/layout/transaction.rs b/src/layout/transaction.rs index 5dfedd9..f6bcc86 100644 --- a/src/layout/transaction.rs +++ b/src/layout/transaction.rs @@ -32,6 +32,9 @@ use crate::{ window::WindowElement, }; +/// The timeout before transactions stop applying. +const TIMEOUT: Duration = Duration::from_millis(1000); + /// Type for window snapshots. pub type LayoutSnapshot = RenderSnapshot>; @@ -56,10 +59,10 @@ pub struct LayoutTransaction { } impl LayoutTransaction { - /// Schedule an event after the timeout to check for stuff. + /// Schedule an event after the timeout to check for readiness. fn register_wakeup(loop_handle: &LoopHandle<'static, State>) { let _ = loop_handle.insert_source( - Timer::from_duration(Duration::from_millis(150)), + Timer::from_duration(TIMEOUT + Duration::from_millis(10)), |_, _, _| TimeoutAction::Drop, ); } @@ -117,12 +120,12 @@ impl LayoutTransaction { /// Returns whether all pending windows have committed their serials or the timeout has been /// reached. pub fn ready(&self) -> bool { - Instant::now().duration_since(self.start_time) >= Duration::from_millis(150) - || (!self.wait - && self - .pending_windows - .iter() - .all(|(win, serial)| win.is_serial_committed(*serial))) + Instant::now().duration_since(self.start_time) >= TIMEOUT + // || (!self.wait + // && self + // .pending_windows + // .iter() + // .all(|(win, serial)| win.is_serial_committed(*serial))) } } diff --git a/src/render/util/snapshot.rs b/src/render/util/snapshot.rs index cd62203..fa21f6f 100644 --- a/src/render/util/snapshot.rs +++ b/src/render/util/snapshot.rs @@ -100,7 +100,7 @@ impl WindowElement { alpha: f32, ) { self.with_state_mut(|state| { - if state.snapshot.is_none() { + if state.snapshot.is_none() || self.is_x11() { let elements = self.render_elements(renderer, location, scale, alpha); state.snapshot = Some(RenderSnapshot::new(elements, scale)); } @@ -117,10 +117,9 @@ pub fn capture_snapshots_on_output( let windows_on_foc_tags = output.with_state(|state| { let focused_tags = state.focused_tags().collect::>(); pinnacle - .windows + .z_index_stack .iter() .rev() - .filter(|win| !win.is_x11_override_redirect()) .filter(|win| { win.with_state(|state| state.tags.iter().any(|tg| focused_tags.contains(&tg))) })