From 39d5454a7fb514e90cfaacb35e2cc664a545bc9d Mon Sep 17 00:00:00 2001 From: Ottatop Date: Thu, 31 Aug 2023 20:35:54 -0500 Subject: [PATCH 01/12] Move schedule to impl --- src/backend/udev.rs | 61 +++++++++++++++++++-------------------- src/handlers/xdg_shell.rs | 3 +- src/state.rs | 45 +++++++++++++++++++---------- 3 files changed, 60 insertions(+), 49 deletions(-) diff --git a/src/backend/udev.rs b/src/backend/udev.rs index 0d3ffae..2d79177 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -853,38 +853,6 @@ impl State { device_id: node, }); - // Run any connected callbacks - { - let clone = output.clone(); - self.loop_handle.insert_idle(|data| { - crate::state::schedule( - data, - |dt| dt.state.api_state.stream.is_some(), - move |dt| { - let stream = dt - .state - .api_state - .stream - .as_ref() - .expect("Stream doesn't exist"); - let mut stream = stream.lock().expect("Couldn't lock stream"); - for callback_id in dt.state.output_callback_ids.iter() { - crate::api::send_to_client( - &mut stream, - &OutgoingMsg::CallCallback { - callback_id: *callback_id, - args: Some(Args::ConnectForAllOutputs { - output_name: clone.name(), - }), - }, - ) - .expect("Send to client failed"); - } - }, - ) - }); - } - let allocator = GbmAllocator::new( device.gbm.clone(), GbmBufferFlags::RENDERING | GbmBufferFlags::SCANOUT, @@ -980,6 +948,35 @@ impl State { device.surfaces.insert(crtc, surface); self.schedule_initial_render(node, crtc, self.loop_handle.clone()); + + // Run any connected callbacks + { + let clone = output.clone(); + self.schedule( + |dt| dt.state.api_state.stream.is_some(), + move |dt| { + let stream = dt + .state + .api_state + .stream + .as_ref() + .expect("Stream doesn't exist"); + let mut stream = stream.lock().expect("Couldn't lock stream"); + for callback_id in dt.state.output_callback_ids.iter() { + crate::api::send_to_client( + &mut stream, + &OutgoingMsg::CallCallback { + callback_id: *callback_id, + args: Some(Args::ConnectForAllOutputs { + output_name: clone.name(), + }), + }, + ) + .expect("Send to client failed"); + } + }, + ); + } } fn connector_disconnected( diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index eba89fc..158b05a 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -91,8 +91,9 @@ impl XdgShellHandler for State { // note to self: don't reorder this // TODO: fix it so that reordering this doesn't break stuff self.windows.push(window.clone()); - // self.space.map_element(window.clone(), (0, 0), true); + if let Some(focused_output) = self.focus_state.focused_output.clone() { + // FIXME: ignoring initial configure here self.update_windows(&focused_output); BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst); tracing::debug!( diff --git a/src/state.rs b/src/state.rs index 9774f93..3a1da88 100644 --- a/src/state.rs +++ b/src/state.rs @@ -159,22 +159,6 @@ where on_commit(data); } -// Schedule something to be done when `condition` returns true. -pub fn schedule(data: &mut CalloopData, condition: F1, run: F2) -where - F1: Fn(&mut CalloopData) -> bool + 'static, - F2: FnOnce(&mut CalloopData) + 'static, -{ - if !condition(data) { - data.state.loop_handle.insert_idle(|data| { - schedule(data, condition, run); - }); - return; - } - - run(data); -} - impl State { pub fn init( backend: Backend, @@ -393,6 +377,35 @@ impl State { xdisplay: None, }) } + + /// Schedule `run` to run when `condition` returns true. + /// + /// This will continually reschedule `run` in the event loop if `condition` returns false. + pub fn schedule(&self, condition: F1, run: F2) + where + F1: Fn(&mut CalloopData) -> bool + 'static, + F2: FnOnce(&mut CalloopData) + 'static, + { + self.loop_handle.insert_idle(|data| { + Self::schedule_inner(data, condition, run); + }); + } + + // Schedule something to be done when `condition` returns true. + fn schedule_inner(data: &mut CalloopData, condition: F1, run: F2) + where + F1: Fn(&mut CalloopData) -> bool + 'static, + F2: FnOnce(&mut CalloopData) + 'static, + { + if !condition(data) { + data.state.loop_handle.insert_idle(|data| { + Self::schedule_inner(data, condition, run); + }); + return; + } + + run(data); + } } fn get_config_dir() -> PathBuf { From 5744654cb1fc3ab47936771efe3298e4cafac1f6 Mon Sep 17 00:00:00 2001 From: Ottatop Date: Thu, 31 Aug 2023 20:42:58 -0500 Subject: [PATCH 02/12] Respect initial configure --- src/handlers/xdg_shell.rs | 96 ++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 37 deletions(-) diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index 158b05a..5e81e44 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -92,49 +92,71 @@ impl XdgShellHandler for State { // TODO: fix it so that reordering this doesn't break stuff self.windows.push(window.clone()); - if let Some(focused_output) = self.focus_state.focused_output.clone() { - // FIXME: ignoring initial configure here - self.update_windows(&focused_output); - BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst); - tracing::debug!( - "blocker {}", - BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst) - ); - for win in windows_on_output.iter() { - if let Some(surf) = win.wl_surface() { - compositor::add_blocker(&surf, crate::window::WindowBlocker); + let win_clone = window.clone(); + self.schedule( + move |_data| { + if let WindowElement::Wayland(window) = &win_clone { + let initial_configure_sent = + compositor::with_states(window.toplevel().wl_surface(), |states| { + states + .data_map + .get::() + .expect("XdgToplevelSurfaceData wasn't in surface's data map") + .lock() + .expect("Failed to lock Mutex") + .initial_configure_sent + }); + + initial_configure_sent + } else { + true } - } - let clone = window.clone(); - self.loop_handle.insert_idle(|data| { - crate::state::schedule_on_commit(data, vec![clone], move |data| { - BLOCKER_COUNTER.store(0, std::sync::atomic::Ordering::SeqCst); + }, + |data| { + if let Some(focused_output) = data.state.focus_state.focused_output.clone() { + data.state.update_windows(&focused_output); + BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst); tracing::debug!( "blocker {}", BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst) ); - for client in windows_on_output - .iter() - .filter_map(|win| win.wl_surface()?.client()) - { - data.state - .client_compositor_state(&client) - .blocker_cleared(&mut data.state, &data.display.handle()) + for win in windows_on_output.iter() { + if let Some(surf) = win.wl_surface() { + compositor::add_blocker(&surf, crate::window::WindowBlocker); + } } - }) - }); - } - self.loop_handle.insert_idle(move |data| { - data.state - .seat - .get_keyboard() - .expect("Seat had no keyboard") // FIXME: actually handle error - .set_focus( - &mut data.state, - Some(FocusTarget::Window(window)), - SERIAL_COUNTER.next_serial(), - ); - }); + let clone = window.clone(); + data.state.loop_handle.insert_idle(|data| { + crate::state::schedule_on_commit(data, vec![clone], move |data| { + BLOCKER_COUNTER.store(0, std::sync::atomic::Ordering::SeqCst); + tracing::debug!( + "blocker {}", + BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst) + ); + for client in windows_on_output + .iter() + .filter_map(|win| win.wl_surface()?.client()) + { + data.state + .client_compositor_state(&client) + .blocker_cleared(&mut data.state, &data.display.handle()) + } + }) + }); + } + data.state.loop_handle.insert_idle(move |data| { + data.state + .seat + .get_keyboard() + .expect("Seat had no keyboard") // FIXME: actually handle error + .set_focus( + &mut data.state, + Some(FocusTarget::Window(window)), + SERIAL_COUNTER.next_serial(), + ); + }); + }, + ); } fn toplevel_destroyed(&mut self, surface: ToplevelSurface) { From 15e03b6c17570be3de757fb5f2c58cf1548ca40e Mon Sep 17 00:00:00 2001 From: Ottatop Date: Sat, 2 Sep 2023 02:46:26 -0500 Subject: [PATCH 03/12] Stop rendering instead of unmapping windows (regression) Layer shell surfaces currently don't render anymore. I may need to manually render them instead of getting them through Space::space_render_elements. --- src/backend/udev.rs | 33 +++++++++++++++------------- src/backend/winit.rs | 12 ++++++----- src/handlers.rs | 2 ++ src/handlers/xdg_shell.rs | 39 +++++++++++++++++++++++++++------ src/layout.rs | 20 +++++++++++------ src/main.rs | 1 - src/render.rs | 31 ++++++++++++++++++--------- src/state.rs | 4 ++++ src/tag.rs | 45 ++++++++++++++++++++++++++++++++++++++- 9 files changed, 141 insertions(+), 46 deletions(-) diff --git a/src/backend/udev.rs b/src/backend/udev.rs index 2d79177..e1da6c7 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -1344,18 +1344,19 @@ impl State { }; let result = render_surface( + &mut self.cursor_status, + &self.space, + &self.windows, + self.dnd_icon.as_ref(), + &self.focus_state.focus_stack, surface, &mut renderer, - &self.space, &output, self.seat.input_method(), - self.pointer_location, &pointer_image, &mut backend.pointer_element, - &mut self.cursor_status, - self.dnd_icon.as_ref(), + self.pointer_location, &self.clock, - &self.focus_state.focus_stack, ); let reschedule = match &result { Ok(has_rendered) => !has_rendered, @@ -1448,30 +1449,32 @@ impl State { #[allow(clippy::too_many_arguments)] fn render_surface<'a>( + cursor_status: &mut CursorImageStatus, + space: &Space, + windows: &[WindowElement], + dnd_icon: Option<&WlSurface>, + focus_stack: &[WindowElement], surface: &'a mut SurfaceData, renderer: &mut UdevRenderer<'a, '_>, - space: &Space, output: &Output, input_method: &InputMethodHandle, - pointer_location: Point, pointer_image: &TextureBuffer, pointer_element: &mut PointerElement, - cursor_status: &mut CursorImageStatus, - dnd_icon: Option<&WlSurface>, + pointer_location: Point, clock: &Clock, - focus_stack: &[WindowElement], ) -> Result { let output_render_elements = crate::render::generate_render_elements( - renderer, space, - output, - input_method, + windows, pointer_location, - pointer_element, - Some(pointer_image), cursor_status, dnd_icon, focus_stack, + renderer, + output, + input_method, + pointer_element, + Some(pointer_image), ); let res = surface.compositor.render_frame::<_, _, GlesTexture>( diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 6a472c7..f9f14fa 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -237,16 +237,17 @@ pub fn run_winit() -> anyhow::Result<()> { *full_redraw = full_redraw.saturating_sub(1); let output_render_elements = crate::render::generate_render_elements( - backend.backend.renderer(), &state.space, - &output, - state.seat.input_method(), + &state.windows, state.pointer_location, - &mut pointer_element, - None, &mut state.cursor_status, state.dnd_icon.as_ref(), &state.focus_state.focus_stack, + backend.backend.renderer(), + &output, + state.seat.input_method(), + &mut pointer_element, + None, ); let render_res = backend.backend.bind().and_then(|_| { @@ -271,6 +272,7 @@ pub fn run_winit() -> anyhow::Result<()> { Ok(render_output_result) => { let has_rendered = render_output_result.damage.is_some(); if let Some(damage) = render_output_result.damage { + // tracing::debug!("damage rects are {damage:?}"); if let Err(err) = backend.backend.submit(Some(&damage)) { tracing::warn!("{}", err); } diff --git a/src/handlers.rs b/src/handlers.rs index df370fb..fc2b61a 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -121,6 +121,7 @@ impl CompositorHandler for State { if let Some(window) = self.window_for_surface(surface) { window.with_state(|state| { if let LocationRequestState::Acknowledged(new_pos) = state.loc_request_state { + tracing::debug!("Mapping Acknowledged window"); state.loc_request_state = LocationRequestState::Idle; self.space.map_element(window.clone(), new_pos, false); } @@ -128,6 +129,7 @@ impl CompositorHandler for State { } // correct focus layering + // TODO: maybe do this at the end of every event loop cycle instead? self.focus_state.fix_up_focus(&mut self.space); } diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index 5e81e44..2afab3a 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -93,6 +93,8 @@ impl XdgShellHandler for State { self.windows.push(window.clone()); let win_clone = window.clone(); + + // Let the initial configure happen before updating the windows self.schedule( move |_data| { if let WindowElement::Wayland(window) = &win_clone { @@ -170,10 +172,8 @@ impl XdgShellHandler for State { self.update_windows(&focused_output); } - // let mut windows: Vec = self.space.elements().cloned().collect(); - // windows.retain(|window| window.toplevel() != &surface); - // Layouts::master_stack(self, windows, crate::layout::Direction::Left); let focus = self.focus_state.current_focus().map(FocusTarget::Window); + self.seat .get_keyboard() .expect("Seat had no keyboard") @@ -192,7 +192,7 @@ impl XdgShellHandler for State { crate::grab::move_grab::move_request_client( self, surface.wl_surface(), - &Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat"), + &Seat::from_resource(&seat).expect("couldn't get seat from WlSeat"), serial, BUTTON_LEFT, ); @@ -209,7 +209,7 @@ impl XdgShellHandler for State { crate::grab::resize_grab::resize_request_client( self, surface.wl_surface(), - &Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat"), + &Seat::from_resource(&seat).expect("couldn't get seat from WlSeat"), serial, edges.into(), BUTTON_LEFT, @@ -230,7 +230,7 @@ impl XdgShellHandler for State { } fn grab(&mut self, surface: PopupSurface, seat: WlSeat, serial: Serial) { - let seat: Seat = Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat"); + let seat: Seat = Seat::from_resource(&seat).expect("couldn't get seat from WlSeat"); let popup_kind = PopupKind::Xdg(surface); if let Some(root) = find_popup_root_surface(&popup_kind).ok().and_then(|root| { self.window_for_surface(&root) @@ -282,18 +282,43 @@ impl XdgShellHandler for State { match &configure { Configure::Toplevel(configure) => { if configure.serial >= serial { - // tracing::debug!("acked configure, new loc is {:?}", new_loc); + tracing::debug!("acked configure, new loc is {:?}", new_loc); state.loc_request_state = LocationRequestState::Acknowledged(new_loc); + + // Send a frame here because it causes (most) unmapped windows to + // commit. I haven't done enough doc diving to know if there's a + // better way. + if let Some(focused_output) = self.focus_state.focused_output.clone() { + tracing::debug!("Sending ack frame to window"); window.send_frame( &focused_output, self.clock.now(), Some(Duration::ZERO), surface_primary_scanout_output, ); + // Forcibly map the window after 16ms if it hasn't committed + let current_time = self.clock.now(); + let win_clone = window.clone(); + self.schedule( + move |data| { + smithay::utils::Time::elapsed( + ¤t_time, + data.state.clock.now(), + ) > Duration::from_millis(100) + }, + move |data| { + win_clone.send_frame( + &focused_output, + data.state.clock.now(), + Some(Duration::ZERO), + surface_primary_scanout_output, + ); + }, + ); } } } diff --git a/src/layout.rs b/src/layout.rs index 080ee6e..40f972a 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -53,6 +53,8 @@ impl State { } } + /// Compute tiled window locations and sizes, size maximized and fullscreen windows correctly, + /// and send configures and that cool stuff. pub fn update_windows(&mut self, output: &Output) { let Some(layout) = output.with_state(|state| { state.focused_tags().next().cloned().map(|tag| tag.layout()) @@ -66,6 +68,9 @@ impl State { }) }); + tracing::debug!("{} on", windows_on_foc_tags.len()); + + // Don't unmap windows that aren't on `output` (that would clear all other monitors) windows_not_on_foc_tags.retain(|win| win.output(self) == Some(output.clone())); let tiled_windows = windows_on_foc_tags @@ -132,13 +137,14 @@ impl State { }); } - self.loop_handle.insert_idle(|data| { - crate::state::schedule_on_commit(data, windows_on_foc_tags, |dt| { - for win in windows_not_on_foc_tags { - dt.state.space.unmap_elem(&win); - } - }) - }); + // self.loop_handle.insert_idle(|data| { + // crate::state::schedule_on_commit(data, windows_on_foc_tags, |dt| { + // for win in windows_not_on_foc_tags { + // // dt.state.space.map_element(win, (500, 0), false); + // dt.state.space.unmap_elem(&win); + // } + // }) + // }); } } diff --git a/src/main.rs b/src/main.rs index c537c44..7ee3a4b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,6 @@ //! 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 #![warn(clippy::unwrap_used)] mod api; diff --git a/src/render.rs b/src/render.rs index b93c3f3..3c5ae6c 100644 --- a/src/render.rs +++ b/src/render.rs @@ -11,7 +11,7 @@ use smithay::{ ImportAll, ImportMem, Renderer, Texture, }, desktop::{ - space::{self, SpaceRenderElements, SurfaceTree}, + space::{SpaceRenderElements, SurfaceTree}, Space, }, input::pointer::{CursorImageAttributes, CursorImageStatus}, @@ -25,7 +25,7 @@ use smithay::{ wayland::{compositor, input_method::InputMethodHandle}, }; -use crate::{state::WithState, window::WindowElement}; +use crate::{state::WithState, tag::Tag, window::WindowElement}; use self::pointer::{PointerElement, PointerRenderElement}; @@ -78,16 +78,17 @@ where #[allow(clippy::too_many_arguments)] pub fn generate_render_elements( - renderer: &mut R, space: &Space, - output: &Output, - input_method: &InputMethodHandle, + windows: &[WindowElement], pointer_location: Point, - pointer_element: &mut PointerElement, - pointer_image: Option<&TextureBuffer>, cursor_status: &mut CursorImageStatus, dnd_icon: Option<&WlSurface>, focus_stack: &[WindowElement], + renderer: &mut R, + output: &Output, + input_method: &InputMethodHandle, + pointer_element: &mut PointerElement, + pointer_image: Option<&TextureBuffer>, ) -> Vec>> where R: Renderer + ImportAll + ImportMem, @@ -211,9 +212,18 @@ where output_render_elements } else { // render everything - let space_render_elements = - space::space_render_elements(renderer, [space], output, 1.0) - .expect("Failed to get render elements"); + // let space_render_elements = + // space::space_render_elements(renderer, [space], output, 1.0) + // .expect("Failed to get render elements"); + + let tags = space + .outputs() + .flat_map(|op| { + op.with_state(|state| state.focused_tags().cloned().collect::>()) + }) + .collect::>(); + let space_render_elements: Vec> = + Tag::tag_render_elements(&tags, windows, space, renderer); let mut output_render_elements = Vec::>>::new(); @@ -226,6 +236,7 @@ where output_render_elements.extend( space_render_elements .into_iter() + .map(CustomRenderElements::from) .map(OutputRenderElements::from), ); output_render_elements diff --git a/src/state.rs b/src/state.rs index 3a1da88..8f785a3 100644 --- a/src/state.rs +++ b/src/state.rs @@ -589,6 +589,10 @@ impl ApiState { pub trait WithState { type State: Default; + + /// Access data map state. + /// + /// RefCell Safety: This function will panic if called within itself. fn with_state(&self, func: F) -> T where F: FnMut(&mut Self::State) -> T; diff --git a/src/tag.rs b/src/tag.rs index 0431a25..536da3a 100644 --- a/src/tag.rs +++ b/src/tag.rs @@ -7,11 +7,20 @@ use std::{ sync::atomic::{AtomicU32, Ordering}, }; -use smithay::output::Output; +use smithay::{ + backend::renderer::{ + element::{surface::WaylandSurfaceRenderElement, AsRenderElements}, + ImportAll, ImportMem, Renderer, + }, + desktop::{space::SpaceElement, Space}, + output::Output, + utils::Scale, +}; use crate::{ layout::Layout, state::{State, WithState}, + window::WindowElement, }; static TAG_ID_COUNTER: AtomicU32 = AtomicU32::new(0); @@ -64,6 +73,7 @@ impl Eq for TagInner {} #[derive(Debug, Clone, PartialEq, Eq)] pub struct Tag(Rc>); +// RefCell Safety: These methods should never panic because they are all self-contained or Copy. impl Tag { pub fn id(&self) -> TagId { self.0.borrow().id @@ -107,4 +117,37 @@ impl Tag { .find(|output| output.with_state(|state| state.tags.iter().any(|tg| tg == self))) .cloned() } + + /// Get the render_elements for the provided tags. + pub fn tag_render_elements( + tags: &[Tag], + windows: &[WindowElement], + space: &Space, + renderer: &mut R, + ) -> Vec + where + R: Renderer + ImportAll + ImportMem, + ::TextureId: 'static, + C: From>, + { + let elements = windows + .iter() + .filter(|win| { + win.with_state(|state| { + state + .tags + .iter() + .any(|tag| tags.iter().any(|tag2| tag == tag2)) + }) + }) + .flat_map(|win| { + let loc = (space.element_location(win).unwrap_or((0, 0).into()) + - win.geometry().loc) + .to_physical(1); + win.render_elements::(renderer, loc, Scale::from(1.0), 1.0) + }) + .collect::>(); + + elements + } } From 41ea5e523020211eea2ae2ffa46ed52d2aaac08e Mon Sep 17 00:00:00 2001 From: Ottatop Date: Sat, 2 Sep 2023 16:51:39 -0500 Subject: [PATCH 04/12] Ignore non-rendered windows for input --- src/input.rs | 19 +++++++++++++++++-- src/render.rs | 2 +- src/tag.rs | 1 + src/window.rs | 8 ++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/input.rs b/src/input.rs index 9e66cf4..dd6b8ef 100644 --- a/src/input.rs +++ b/src/input.rs @@ -73,6 +73,7 @@ impl State { } } + /// Get the [`FocusTarget`] under `point`. pub fn surface_under

(&self, point: P) -> Option<(FocusTarget, Point)> where P: Into>, @@ -114,8 +115,22 @@ impl State { }) .or_else(|| { self.space - .element_under(point) - .map(|(window, loc)| (window.clone().into(), loc)) + .elements() + .rev() + .filter(|win| win.is_on_active_tag(self.space.outputs())) + .find_map(|win| { + let loc = self + .space + .element_location(win) + .expect("called elem loc on unmapped win") + - win.geometry().loc; + + if win.is_in_input_region(&(point - loc.to_f64())) { + Some((win.clone().into(), loc)) + } else { + None + } + }) }) .or_else(|| { layers diff --git a/src/render.rs b/src/render.rs index 3c5ae6c..3be0410 100644 --- a/src/render.rs +++ b/src/render.rs @@ -226,7 +226,7 @@ where Tag::tag_render_elements(&tags, windows, space, renderer); let mut output_render_elements = - Vec::>>::new(); + Vec::>>::new(); output_render_elements.extend( custom_render_elements diff --git a/src/tag.rs b/src/tag.rs index 536da3a..0acd870 100644 --- a/src/tag.rs +++ b/src/tag.rs @@ -141,6 +141,7 @@ impl Tag { }) }) .flat_map(|win| { + // subtract win.geometry().loc to align decorations correctly let loc = (space.element_location(win).unwrap_or((0, 0).into()) - win.geometry().loc) .to_physical(1); diff --git a/src/window.rs b/src/window.rs index f05578b..e83a215 100644 --- a/src/window.rs +++ b/src/window.rs @@ -249,6 +249,14 @@ impl WindowElement { self.with_state(|st| st.tags.first().and_then(|tag| tag.output(state))) } + /// RefCell Safety: This uses RefCells on both `self` and everything in `outputs`. + pub fn is_on_active_tag<'a>(&self, outputs: impl IntoIterator) -> bool { + let mut tags = outputs.into_iter().flat_map(|op| { + op.with_state(|state| state.focused_tags().cloned().collect::>()) + }); + self.with_state(|state| state.tags.iter().any(|tag| tags.any(|tag2| tag == &tag2))) + } + /// Returns `true` if the window element is [`Wayland`]. /// /// [`Wayland`]: WindowElement::Wayland From 9152145bbaafb79889361cac55b33fb8c91e99fb Mon Sep 17 00:00:00 2001 From: Ottatop Date: Sat, 2 Sep 2023 17:49:57 -0500 Subject: [PATCH 05/12] Render layer surfaces properly --- src/input.rs | 5 ++++ src/render.rs | 74 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/src/input.rs b/src/input.rs index dd6b8ef..d7b2482 100644 --- a/src/input.rs +++ b/src/input.rs @@ -102,9 +102,12 @@ impl State { }); // I think I'm going a bit too far with the functional stuff + + // The topmost fullscreen window top_fullscreen_window .map(|window| (FocusTarget::from(window.clone()), output_geo.loc)) .or_else(|| { + // The topmost layer surface in Overlay or Top layers .layer_under(wlr_layer::Layer::Overlay, point) .or_else(|| layers.layer_under(wlr_layer::Layer::Top, point)) @@ -114,6 +117,7 @@ impl State { }) }) .or_else(|| { + // The topmost window self.space .elements() .rev() @@ -133,6 +137,7 @@ impl State { }) }) .or_else(|| { + // The topmost layer surface in Bottom or Background layers .layer_under(wlr_layer::Layer::Bottom, point) .or_else(|| layers.layer_under(wlr_layer::Layer::Background, point)) diff --git a/src/render.rs b/src/render.rs index 3be0410..46cabe3 100644 --- a/src/render.rs +++ b/src/render.rs @@ -11,6 +11,7 @@ use smithay::{ ImportAll, ImportMem, Renderer, Texture, }, desktop::{ + layer_map_for_output, space::{SpaceRenderElements, SurfaceTree}, Space, }, @@ -22,7 +23,7 @@ use smithay::{ }, render_elements, utils::{IsAlive, Logical, Physical, Point, Scale}, - wayland::{compositor, input_method::InputMethodHandle}, + wayland::{compositor, input_method::InputMethodHandle, shell::wlr_layer}, }; use crate::{state::WithState, tag::Tag, window::WindowElement}; @@ -76,6 +77,58 @@ where } } +struct LayerRenderElements { + background: Vec>, + bottom: Vec>, + top: Vec>, + overlay: Vec>, +} + +fn layer_render_elements(output: &Output, renderer: &mut R) -> LayerRenderElements +where + R: Renderer + ImportAll, + ::TextureId: 'static, +{ + let layer_map = layer_map_for_output(output); + let mut overlay = vec![]; + let mut top = vec![]; + let mut bottom = vec![]; + let mut background = vec![]; + + let layer_elements = layer_map + .layers() + .filter_map(|surface| { + layer_map + .layer_geometry(surface) + .map(|geo| (surface, geo.loc)) + }) + .map(|(surface, loc)| { + let render_elements = surface.render_elements::>( + renderer, + loc.to_physical(1), + Scale::from(1.0), + 1.0, + ); + (surface.layer(), render_elements) + }); + + for (layer, elements) in layer_elements { + match layer { + wlr_layer::Layer::Background => background.extend(elements), + wlr_layer::Layer::Bottom => bottom.extend(elements), + wlr_layer::Layer::Top => top.extend(elements), + wlr_layer::Layer::Overlay => overlay.extend(elements), + } + } + + LayerRenderElements { + background, + bottom, + top, + overlay, + } +} + #[allow(clippy::too_many_arguments)] pub fn generate_render_elements( space: &Space, @@ -216,29 +269,44 @@ where // space::space_render_elements(renderer, [space], output, 1.0) // .expect("Failed to get render elements"); + let LayerRenderElements { + background, + bottom, + top, + overlay, + } = layer_render_elements(output, renderer); + let tags = space .outputs() .flat_map(|op| { op.with_state(|state| state.focused_tags().cloned().collect::>()) }) .collect::>(); - let space_render_elements: Vec> = + let window_render_elements: Vec> = Tag::tag_render_elements(&tags, windows, space, renderer); let mut output_render_elements = Vec::>>::new(); + // Elements render from top to bottom + output_render_elements.extend( custom_render_elements .into_iter() .map(OutputRenderElements::from), ); + output_render_elements.extend( - space_render_elements + overlay .into_iter() + .chain(top) + .chain(window_render_elements) + .chain(bottom) + .chain(background) .map(CustomRenderElements::from) .map(OutputRenderElements::from), ); + output_render_elements } }; From ce5ed0db69af8296a2b3977b72b951e828755bd2 Mon Sep 17 00:00:00 2001 From: Ottatop Date: Sat, 2 Sep 2023 18:35:35 -0500 Subject: [PATCH 06/12] Clean up stuff --- src/render.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/render.rs b/src/render.rs index 46cabe3..c2b8a1e 100644 --- a/src/render.rs +++ b/src/render.rs @@ -264,11 +264,6 @@ where output_render_elements } else { - // render everything - // let space_render_elements = - // space::space_render_elements(renderer, [space], output, 1.0) - // .expect("Failed to get render elements"); - let LayerRenderElements { background, bottom, From 87a8f29298a0ad6b638ee8aa86302b18a930696a Mon Sep 17 00:00:00 2001 From: Ottatop Date: Sat, 2 Sep 2023 20:26:11 -0500 Subject: [PATCH 07/12] Fix reversed order of window rendering --- src/backend/winit.rs | 2 ++ src/focus.rs | 4 +--- src/handlers.rs | 2 +- src/layout.rs | 32 +++++++++++++++++++++++--------- src/render.rs | 8 +------- src/tag.rs | 11 ++--------- 6 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/backend/winit.rs b/src/backend/winit.rs index f9f14fa..fb25010 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -236,6 +236,8 @@ pub fn run_winit() -> anyhow::Result<()> { let full_redraw = &mut backend.full_redraw; *full_redraw = full_redraw.saturating_sub(1); + state.focus_state.fix_up_focus(&mut state.space); + let output_render_elements = crate::render::generate_render_elements( &state.space, &state.windows, diff --git a/src/focus.rs b/src/focus.rs index 0464958..25739b8 100644 --- a/src/focus.rs +++ b/src/focus.rs @@ -50,9 +50,7 @@ impl FocusState { /// to back to correct their z locations. pub fn fix_up_focus(&self, space: &mut Space) { for win in self.focus_stack.iter() { - if let Some(loc) = space.element_location(win) { - space.map_element(win.clone(), loc, false); - } + space.raise_element(win, false); } } } diff --git a/src/handlers.rs b/src/handlers.rs index fc2b61a..23133e4 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -130,7 +130,7 @@ impl CompositorHandler for State { // correct focus layering // TODO: maybe do this at the end of every event loop cycle instead? - self.focus_state.fix_up_focus(&mut self.space); + // self.focus_state.fix_up_focus(&mut self.space); } fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState { diff --git a/src/layout.rs b/src/layout.rs index 40f972a..13f6e97 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -2,7 +2,7 @@ use itertools::{Either, Itertools}; use smithay::{ - desktop::layer_map_for_output, + desktop::{layer_map_for_output, space::SpaceElement}, output::Output, utils::{Logical, Point, Rectangle, Size}, }; @@ -462,13 +462,27 @@ impl State { } } - // TODO: don't use the focused output, use the outputs the two windows are on - let output = self - .focus_state - .focused_output - .clone() - .expect("no focused output"); - self.update_windows(&output); - // self.re_layout(&output); + // Some windows just don't want to commit on a timely basis, like VS Code on Wayland, + // unless their sizes change. In this case, if the two windows have the same size, + // just map them to the other's location instead of going through update_windows(). + if win1.geometry().size == win2.geometry().size { + let win1_loc = self.space.element_location(win1); + let win2_loc = self.space.element_location(win2); + + if let Some(win1_loc) = win1_loc { + if let Some(win2_loc) = win2_loc { + self.space.map_element(win1.clone(), win2_loc, false); + self.space.map_element(win2.clone(), win1_loc, false); + } + } + } else { + // TODO: don't use the focused output, use the outputs the two windows are on + let output = self + .focus_state + .focused_output + .clone() + .expect("no focused output"); + self.update_windows(&output); + } } } diff --git a/src/render.rs b/src/render.rs index c2b8a1e..b0fc997 100644 --- a/src/render.rs +++ b/src/render.rs @@ -271,14 +271,8 @@ where overlay, } = layer_render_elements(output, renderer); - let tags = space - .outputs() - .flat_map(|op| { - op.with_state(|state| state.focused_tags().cloned().collect::>()) - }) - .collect::>(); let window_render_elements: Vec> = - Tag::tag_render_elements(&tags, windows, space, renderer); + Tag::tag_render_elements(windows, space, renderer); let mut output_render_elements = Vec::>>::new(); diff --git a/src/tag.rs b/src/tag.rs index 0acd870..39c6ab1 100644 --- a/src/tag.rs +++ b/src/tag.rs @@ -120,7 +120,6 @@ impl Tag { /// Get the render_elements for the provided tags. pub fn tag_render_elements( - tags: &[Tag], windows: &[WindowElement], space: &Space, renderer: &mut R, @@ -132,14 +131,8 @@ impl Tag { { let elements = windows .iter() - .filter(|win| { - win.with_state(|state| { - state - .tags - .iter() - .any(|tag| tags.iter().any(|tag2| tag == tag2)) - }) - }) + .rev() // rev because I treat the focus stack backwards vs how the renderer orders it + .filter(|win| win.is_on_active_tag(space.outputs())) .flat_map(|win| { // subtract win.geometry().loc to align decorations correctly let loc = (space.element_location(win).unwrap_or((0, 0).into()) From 44d87d4725a6b456d41fe2e5fd8c9daae203dd9f Mon Sep 17 00:00:00 2001 From: Ottatop Date: Sat, 2 Sep 2023 21:45:03 -0500 Subject: [PATCH 08/12] Fix black screen on discord on wayland --- src/handlers/xdg_shell.rs | 3 +++ src/layout.rs | 31 ++++++++----------------------- src/render.rs | 10 ++++++++++ 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index 2afab3a..02cb675 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -92,6 +92,8 @@ impl XdgShellHandler for State { // TODO: fix it so that reordering this doesn't break stuff self.windows.push(window.clone()); + self.space.map_element(window.clone(), (0, 0), true); + let win_clone = window.clone(); // Let the initial configure happen before updating the windows @@ -115,6 +117,7 @@ impl XdgShellHandler for State { } }, |data| { + tracing::debug!("UPDATING WINDOWS"); if let Some(focused_output) = data.state.focus_state.focused_output.clone() { data.state.update_windows(&focused_output); BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst); diff --git a/src/layout.rs b/src/layout.rs index 13f6e97..4ac2ef6 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -2,7 +2,7 @@ use itertools::{Either, Itertools}; use smithay::{ - desktop::{layer_map_for_output, space::SpaceElement}, + desktop::layer_map_for_output, output::Output, utils::{Logical, Point, Rectangle, Size}, }; @@ -462,27 +462,12 @@ impl State { } } - // Some windows just don't want to commit on a timely basis, like VS Code on Wayland, - // unless their sizes change. In this case, if the two windows have the same size, - // just map them to the other's location instead of going through update_windows(). - if win1.geometry().size == win2.geometry().size { - let win1_loc = self.space.element_location(win1); - let win2_loc = self.space.element_location(win2); - - if let Some(win1_loc) = win1_loc { - if let Some(win2_loc) = win2_loc { - self.space.map_element(win1.clone(), win2_loc, false); - self.space.map_element(win2.clone(), win1_loc, false); - } - } - } else { - // TODO: don't use the focused output, use the outputs the two windows are on - let output = self - .focus_state - .focused_output - .clone() - .expect("no focused output"); - self.update_windows(&output); - } + // TODO: don't use the focused output, use the outputs the two windows are on + let output = self + .focus_state + .focused_output + .clone() + .expect("no focused output"); + self.update_windows(&output); } } diff --git a/src/render.rs b/src/render.rs index b0fc997..6663d3a 100644 --- a/src/render.rs +++ b/src/render.rs @@ -277,6 +277,10 @@ where let mut output_render_elements = Vec::>>::new(); + // let space_render_elements = + // smithay::desktop::space::space_render_elements(renderer, [space], output, 1.0) + // .expect("failed to get space_render_elements"); + // Elements render from top to bottom output_render_elements.extend( @@ -296,6 +300,12 @@ where .map(OutputRenderElements::from), ); + // output_render_elements.extend( + // space_render_elements + // .into_iter() + // .map(OutputRenderElements::from), + // ); + output_render_elements } }; From 92e2886d45e8f21b44a7eb9ad9da5ce10833cb79 Mon Sep 17 00:00:00 2001 From: Ottatop Date: Mon, 4 Sep 2023 01:54:10 -0500 Subject: [PATCH 09/12] Re-add stuff I removed --- src/layout.rs | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/layout.rs b/src/layout.rs index 4ac2ef6..5221947 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -462,12 +462,36 @@ impl State { } } - // TODO: don't use the focused output, use the outputs the two windows are on - let output = self - .focus_state - .focused_output - .clone() - .expect("no focused output"); - self.update_windows(&output); + let mut same_suggested_size = false; + + if let WindowElement::Wayland(w1) = win1 { + if let WindowElement::Wayland(w2) = win2 { + if let Some(w1_size) = w1.toplevel().current_state().size { + if let Some(w2_size) = w2.toplevel().current_state().size { + same_suggested_size = w1_size == w2_size; + } + } + } + } + + if same_suggested_size { + let win1_loc = self.space.element_location(win1); + let win2_loc = self.space.element_location(win2); + + if let Some(win1_loc) = win1_loc { + if let Some(win2_loc) = win2_loc { + self.space.map_element(win1.clone(), win2_loc, false); + self.space.map_element(win2.clone(), win1_loc, false); + } + } + } else { + // TODO: don't use the focused output, use the outputs the two windows are on + let output = self + .focus_state + .focused_output + .clone() + .expect("no focused output"); + self.update_windows(&output); + } } } From 57402958170d3def363a50abcd63075381796d56 Mon Sep 17 00:00:00 2001 From: Ottatop Date: Mon, 4 Sep 2023 02:00:12 -0500 Subject: [PATCH 10/12] Revert "Stop rendering instead of unmapping windows (regression)" This reverts commit 15e03b6c17570be3de757fb5f2c58cf1548ca40e. --- src/backend/udev.rs | 33 ++++++------ src/backend/winit.rs | 14 ++--- src/focus.rs | 4 +- src/handlers.rs | 4 +- src/handlers/xdg_shell.rs | 42 +++------------ src/input.rs | 24 +-------- src/layout.rs | 59 ++++++--------------- src/main.rs | 1 + src/render.rs | 106 +++++--------------------------------- src/state.rs | 4 -- src/tag.rs | 39 +------------- src/window.rs | 8 --- 12 files changed, 64 insertions(+), 274 deletions(-) diff --git a/src/backend/udev.rs b/src/backend/udev.rs index e1da6c7..2d79177 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -1344,19 +1344,18 @@ impl State { }; let result = render_surface( - &mut self.cursor_status, - &self.space, - &self.windows, - self.dnd_icon.as_ref(), - &self.focus_state.focus_stack, surface, &mut renderer, + &self.space, &output, self.seat.input_method(), + self.pointer_location, &pointer_image, &mut backend.pointer_element, - self.pointer_location, + &mut self.cursor_status, + self.dnd_icon.as_ref(), &self.clock, + &self.focus_state.focus_stack, ); let reschedule = match &result { Ok(has_rendered) => !has_rendered, @@ -1449,32 +1448,30 @@ impl State { #[allow(clippy::too_many_arguments)] fn render_surface<'a>( - cursor_status: &mut CursorImageStatus, - space: &Space, - windows: &[WindowElement], - dnd_icon: Option<&WlSurface>, - focus_stack: &[WindowElement], surface: &'a mut SurfaceData, renderer: &mut UdevRenderer<'a, '_>, + space: &Space, output: &Output, input_method: &InputMethodHandle, + pointer_location: Point, pointer_image: &TextureBuffer, pointer_element: &mut PointerElement, - pointer_location: Point, + cursor_status: &mut CursorImageStatus, + dnd_icon: Option<&WlSurface>, clock: &Clock, + focus_stack: &[WindowElement], ) -> Result { let output_render_elements = crate::render::generate_render_elements( + renderer, space, - windows, + output, + input_method, pointer_location, + pointer_element, + Some(pointer_image), cursor_status, dnd_icon, focus_stack, - renderer, - output, - input_method, - pointer_element, - Some(pointer_image), ); let res = surface.compositor.render_frame::<_, _, GlesTexture>( diff --git a/src/backend/winit.rs b/src/backend/winit.rs index fb25010..6a472c7 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -236,20 +236,17 @@ pub fn run_winit() -> anyhow::Result<()> { let full_redraw = &mut backend.full_redraw; *full_redraw = full_redraw.saturating_sub(1); - state.focus_state.fix_up_focus(&mut state.space); - let output_render_elements = crate::render::generate_render_elements( + backend.backend.renderer(), &state.space, - &state.windows, + &output, + state.seat.input_method(), state.pointer_location, + &mut pointer_element, + None, &mut state.cursor_status, state.dnd_icon.as_ref(), &state.focus_state.focus_stack, - backend.backend.renderer(), - &output, - state.seat.input_method(), - &mut pointer_element, - None, ); let render_res = backend.backend.bind().and_then(|_| { @@ -274,7 +271,6 @@ pub fn run_winit() -> anyhow::Result<()> { Ok(render_output_result) => { let has_rendered = render_output_result.damage.is_some(); if let Some(damage) = render_output_result.damage { - // tracing::debug!("damage rects are {damage:?}"); if let Err(err) = backend.backend.submit(Some(&damage)) { tracing::warn!("{}", err); } diff --git a/src/focus.rs b/src/focus.rs index 25739b8..0464958 100644 --- a/src/focus.rs +++ b/src/focus.rs @@ -50,7 +50,9 @@ impl FocusState { /// to back to correct their z locations. pub fn fix_up_focus(&self, space: &mut Space) { for win in self.focus_stack.iter() { - space.raise_element(win, false); + if let Some(loc) = space.element_location(win) { + space.map_element(win.clone(), loc, false); + } } } } diff --git a/src/handlers.rs b/src/handlers.rs index 23133e4..df370fb 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -121,7 +121,6 @@ impl CompositorHandler for State { if let Some(window) = self.window_for_surface(surface) { window.with_state(|state| { if let LocationRequestState::Acknowledged(new_pos) = state.loc_request_state { - tracing::debug!("Mapping Acknowledged window"); state.loc_request_state = LocationRequestState::Idle; self.space.map_element(window.clone(), new_pos, false); } @@ -129,8 +128,7 @@ impl CompositorHandler for State { } // correct focus layering - // TODO: maybe do this at the end of every event loop cycle instead? - // self.focus_state.fix_up_focus(&mut self.space); + self.focus_state.fix_up_focus(&mut self.space); } fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState { diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index 02cb675..5e81e44 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -92,11 +92,7 @@ impl XdgShellHandler for State { // TODO: fix it so that reordering this doesn't break stuff self.windows.push(window.clone()); - self.space.map_element(window.clone(), (0, 0), true); - let win_clone = window.clone(); - - // Let the initial configure happen before updating the windows self.schedule( move |_data| { if let WindowElement::Wayland(window) = &win_clone { @@ -117,7 +113,6 @@ impl XdgShellHandler for State { } }, |data| { - tracing::debug!("UPDATING WINDOWS"); if let Some(focused_output) = data.state.focus_state.focused_output.clone() { data.state.update_windows(&focused_output); BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst); @@ -175,8 +170,10 @@ impl XdgShellHandler for State { self.update_windows(&focused_output); } + // let mut windows: Vec = self.space.elements().cloned().collect(); + // windows.retain(|window| window.toplevel() != &surface); + // Layouts::master_stack(self, windows, crate::layout::Direction::Left); let focus = self.focus_state.current_focus().map(FocusTarget::Window); - self.seat .get_keyboard() .expect("Seat had no keyboard") @@ -195,7 +192,7 @@ impl XdgShellHandler for State { crate::grab::move_grab::move_request_client( self, surface.wl_surface(), - &Seat::from_resource(&seat).expect("couldn't get seat from WlSeat"), + &Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat"), serial, BUTTON_LEFT, ); @@ -212,7 +209,7 @@ impl XdgShellHandler for State { crate::grab::resize_grab::resize_request_client( self, surface.wl_surface(), - &Seat::from_resource(&seat).expect("couldn't get seat from WlSeat"), + &Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat"), serial, edges.into(), BUTTON_LEFT, @@ -233,7 +230,7 @@ impl XdgShellHandler for State { } fn grab(&mut self, surface: PopupSurface, seat: WlSeat, serial: Serial) { - let seat: Seat = Seat::from_resource(&seat).expect("couldn't get seat from WlSeat"); + let seat: Seat = Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat"); let popup_kind = PopupKind::Xdg(surface); if let Some(root) = find_popup_root_surface(&popup_kind).ok().and_then(|root| { self.window_for_surface(&root) @@ -285,43 +282,18 @@ impl XdgShellHandler for State { match &configure { Configure::Toplevel(configure) => { if configure.serial >= serial { - tracing::debug!("acked configure, new loc is {:?}", new_loc); + // tracing::debug!("acked configure, new loc is {:?}", new_loc); state.loc_request_state = LocationRequestState::Acknowledged(new_loc); - - // Send a frame here because it causes (most) unmapped windows to - // commit. I haven't done enough doc diving to know if there's a - // better way. - if let Some(focused_output) = self.focus_state.focused_output.clone() { - tracing::debug!("Sending ack frame to window"); window.send_frame( &focused_output, self.clock.now(), Some(Duration::ZERO), surface_primary_scanout_output, ); - // Forcibly map the window after 16ms if it hasn't committed - let current_time = self.clock.now(); - let win_clone = window.clone(); - self.schedule( - move |data| { - smithay::utils::Time::elapsed( - ¤t_time, - data.state.clock.now(), - ) > Duration::from_millis(100) - }, - move |data| { - win_clone.send_frame( - &focused_output, - data.state.clock.now(), - Some(Duration::ZERO), - surface_primary_scanout_output, - ); - }, - ); } } } diff --git a/src/input.rs b/src/input.rs index d7b2482..9e66cf4 100644 --- a/src/input.rs +++ b/src/input.rs @@ -73,7 +73,6 @@ impl State { } } - /// Get the [`FocusTarget`] under `point`. pub fn surface_under

(&self, point: P) -> Option<(FocusTarget, Point)> where P: Into>, @@ -102,12 +101,9 @@ impl State { }); // I think I'm going a bit too far with the functional stuff - - // The topmost fullscreen window top_fullscreen_window .map(|window| (FocusTarget::from(window.clone()), output_geo.loc)) .or_else(|| { - // The topmost layer surface in Overlay or Top layers .layer_under(wlr_layer::Layer::Overlay, point) .or_else(|| layers.layer_under(wlr_layer::Layer::Top, point)) @@ -117,27 +113,11 @@ impl State { }) }) .or_else(|| { - // The topmost window self.space - .elements() - .rev() - .filter(|win| win.is_on_active_tag(self.space.outputs())) - .find_map(|win| { - let loc = self - .space - .element_location(win) - .expect("called elem loc on unmapped win") - - win.geometry().loc; - - if win.is_in_input_region(&(point - loc.to_f64())) { - Some((win.clone().into(), loc)) - } else { - None - } - }) + .element_under(point) + .map(|(window, loc)| (window.clone().into(), loc)) }) .or_else(|| { - // The topmost layer surface in Bottom or Background layers .layer_under(wlr_layer::Layer::Bottom, point) .or_else(|| layers.layer_under(wlr_layer::Layer::Background, point)) diff --git a/src/layout.rs b/src/layout.rs index 5221947..080ee6e 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -53,8 +53,6 @@ impl State { } } - /// Compute tiled window locations and sizes, size maximized and fullscreen windows correctly, - /// and send configures and that cool stuff. pub fn update_windows(&mut self, output: &Output) { let Some(layout) = output.with_state(|state| { state.focused_tags().next().cloned().map(|tag| tag.layout()) @@ -68,9 +66,6 @@ impl State { }) }); - tracing::debug!("{} on", windows_on_foc_tags.len()); - - // Don't unmap windows that aren't on `output` (that would clear all other monitors) windows_not_on_foc_tags.retain(|win| win.output(self) == Some(output.clone())); let tiled_windows = windows_on_foc_tags @@ -137,14 +132,13 @@ impl State { }); } - // self.loop_handle.insert_idle(|data| { - // crate::state::schedule_on_commit(data, windows_on_foc_tags, |dt| { - // for win in windows_not_on_foc_tags { - // // dt.state.space.map_element(win, (500, 0), false); - // dt.state.space.unmap_elem(&win); - // } - // }) - // }); + self.loop_handle.insert_idle(|data| { + crate::state::schedule_on_commit(data, windows_on_foc_tags, |dt| { + for win in windows_not_on_foc_tags { + dt.state.space.unmap_elem(&win); + } + }) + }); } } @@ -462,36 +456,13 @@ impl State { } } - let mut same_suggested_size = false; - - if let WindowElement::Wayland(w1) = win1 { - if let WindowElement::Wayland(w2) = win2 { - if let Some(w1_size) = w1.toplevel().current_state().size { - if let Some(w2_size) = w2.toplevel().current_state().size { - same_suggested_size = w1_size == w2_size; - } - } - } - } - - if same_suggested_size { - let win1_loc = self.space.element_location(win1); - let win2_loc = self.space.element_location(win2); - - if let Some(win1_loc) = win1_loc { - if let Some(win2_loc) = win2_loc { - self.space.map_element(win1.clone(), win2_loc, false); - self.space.map_element(win2.clone(), win1_loc, false); - } - } - } else { - // TODO: don't use the focused output, use the outputs the two windows are on - let output = self - .focus_state - .focused_output - .clone() - .expect("no focused output"); - self.update_windows(&output); - } + // TODO: don't use the focused output, use the outputs the two windows are on + let output = self + .focus_state + .focused_output + .clone() + .expect("no focused output"); + self.update_windows(&output); + // self.re_layout(&output); } } diff --git a/src/main.rs b/src/main.rs index 7ee3a4b..c537c44 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +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 #![warn(clippy::unwrap_used)] mod api; diff --git a/src/render.rs b/src/render.rs index 6663d3a..b93c3f3 100644 --- a/src/render.rs +++ b/src/render.rs @@ -11,8 +11,7 @@ use smithay::{ ImportAll, ImportMem, Renderer, Texture, }, desktop::{ - layer_map_for_output, - space::{SpaceRenderElements, SurfaceTree}, + space::{self, SpaceRenderElements, SurfaceTree}, Space, }, input::pointer::{CursorImageAttributes, CursorImageStatus}, @@ -23,10 +22,10 @@ use smithay::{ }, render_elements, utils::{IsAlive, Logical, Physical, Point, Scale}, - wayland::{compositor, input_method::InputMethodHandle, shell::wlr_layer}, + wayland::{compositor, input_method::InputMethodHandle}, }; -use crate::{state::WithState, tag::Tag, window::WindowElement}; +use crate::{state::WithState, window::WindowElement}; use self::pointer::{PointerElement, PointerRenderElement}; @@ -77,71 +76,18 @@ where } } -struct LayerRenderElements { - background: Vec>, - bottom: Vec>, - top: Vec>, - overlay: Vec>, -} - -fn layer_render_elements(output: &Output, renderer: &mut R) -> LayerRenderElements -where - R: Renderer + ImportAll, - ::TextureId: 'static, -{ - let layer_map = layer_map_for_output(output); - let mut overlay = vec![]; - let mut top = vec![]; - let mut bottom = vec![]; - let mut background = vec![]; - - let layer_elements = layer_map - .layers() - .filter_map(|surface| { - layer_map - .layer_geometry(surface) - .map(|geo| (surface, geo.loc)) - }) - .map(|(surface, loc)| { - let render_elements = surface.render_elements::>( - renderer, - loc.to_physical(1), - Scale::from(1.0), - 1.0, - ); - (surface.layer(), render_elements) - }); - - for (layer, elements) in layer_elements { - match layer { - wlr_layer::Layer::Background => background.extend(elements), - wlr_layer::Layer::Bottom => bottom.extend(elements), - wlr_layer::Layer::Top => top.extend(elements), - wlr_layer::Layer::Overlay => overlay.extend(elements), - } - } - - LayerRenderElements { - background, - bottom, - top, - overlay, - } -} - #[allow(clippy::too_many_arguments)] pub fn generate_render_elements( + renderer: &mut R, space: &Space, - windows: &[WindowElement], + output: &Output, + input_method: &InputMethodHandle, pointer_location: Point, + pointer_element: &mut PointerElement, + pointer_image: Option<&TextureBuffer>, cursor_status: &mut CursorImageStatus, dnd_icon: Option<&WlSurface>, focus_stack: &[WindowElement], - renderer: &mut R, - output: &Output, - input_method: &InputMethodHandle, - pointer_element: &mut PointerElement, - pointer_image: Option<&TextureBuffer>, ) -> Vec>> where R: Renderer + ImportAll + ImportMem, @@ -264,48 +210,24 @@ where output_render_elements } else { - let LayerRenderElements { - background, - bottom, - top, - overlay, - } = layer_render_elements(output, renderer); - - let window_render_elements: Vec> = - Tag::tag_render_elements(windows, space, renderer); + // render everything + let space_render_elements = + space::space_render_elements(renderer, [space], output, 1.0) + .expect("Failed to get render elements"); let mut output_render_elements = - Vec::>>::new(); - - // let space_render_elements = - // smithay::desktop::space::space_render_elements(renderer, [space], output, 1.0) - // .expect("failed to get space_render_elements"); - - // Elements render from top to bottom + Vec::>>::new(); output_render_elements.extend( custom_render_elements .into_iter() .map(OutputRenderElements::from), ); - output_render_elements.extend( - overlay + space_render_elements .into_iter() - .chain(top) - .chain(window_render_elements) - .chain(bottom) - .chain(background) - .map(CustomRenderElements::from) .map(OutputRenderElements::from), ); - - // output_render_elements.extend( - // space_render_elements - // .into_iter() - // .map(OutputRenderElements::from), - // ); - output_render_elements } }; diff --git a/src/state.rs b/src/state.rs index 8f785a3..3a1da88 100644 --- a/src/state.rs +++ b/src/state.rs @@ -589,10 +589,6 @@ impl ApiState { pub trait WithState { type State: Default; - - /// Access data map state. - /// - /// RefCell Safety: This function will panic if called within itself. fn with_state(&self, func: F) -> T where F: FnMut(&mut Self::State) -> T; diff --git a/src/tag.rs b/src/tag.rs index 39c6ab1..0431a25 100644 --- a/src/tag.rs +++ b/src/tag.rs @@ -7,20 +7,11 @@ use std::{ sync::atomic::{AtomicU32, Ordering}, }; -use smithay::{ - backend::renderer::{ - element::{surface::WaylandSurfaceRenderElement, AsRenderElements}, - ImportAll, ImportMem, Renderer, - }, - desktop::{space::SpaceElement, Space}, - output::Output, - utils::Scale, -}; +use smithay::output::Output; use crate::{ layout::Layout, state::{State, WithState}, - window::WindowElement, }; static TAG_ID_COUNTER: AtomicU32 = AtomicU32::new(0); @@ -73,7 +64,6 @@ impl Eq for TagInner {} #[derive(Debug, Clone, PartialEq, Eq)] pub struct Tag(Rc>); -// RefCell Safety: These methods should never panic because they are all self-contained or Copy. impl Tag { pub fn id(&self) -> TagId { self.0.borrow().id @@ -117,31 +107,4 @@ impl Tag { .find(|output| output.with_state(|state| state.tags.iter().any(|tg| tg == self))) .cloned() } - - /// Get the render_elements for the provided tags. - pub fn tag_render_elements( - windows: &[WindowElement], - space: &Space, - renderer: &mut R, - ) -> Vec - where - R: Renderer + ImportAll + ImportMem, - ::TextureId: 'static, - C: From>, - { - let elements = windows - .iter() - .rev() // rev because I treat the focus stack backwards vs how the renderer orders it - .filter(|win| win.is_on_active_tag(space.outputs())) - .flat_map(|win| { - // subtract win.geometry().loc to align decorations correctly - let loc = (space.element_location(win).unwrap_or((0, 0).into()) - - win.geometry().loc) - .to_physical(1); - win.render_elements::(renderer, loc, Scale::from(1.0), 1.0) - }) - .collect::>(); - - elements - } } diff --git a/src/window.rs b/src/window.rs index e83a215..f05578b 100644 --- a/src/window.rs +++ b/src/window.rs @@ -249,14 +249,6 @@ impl WindowElement { self.with_state(|st| st.tags.first().and_then(|tag| tag.output(state))) } - /// RefCell Safety: This uses RefCells on both `self` and everything in `outputs`. - pub fn is_on_active_tag<'a>(&self, outputs: impl IntoIterator) -> bool { - let mut tags = outputs.into_iter().flat_map(|op| { - op.with_state(|state| state.focused_tags().cloned().collect::>()) - }); - self.with_state(|state| state.tags.iter().any(|tag| tags.any(|tag2| tag == &tag2))) - } - /// Returns `true` if the window element is [`Wayland`]. /// /// [`Wayland`]: WindowElement::Wayland From 088a1f7facfe638ca256160d499ffedd25cd6fb1 Mon Sep 17 00:00:00 2001 From: Ottatop Date: Mon, 4 Sep 2023 02:01:56 -0500 Subject: [PATCH 11/12] Map window in new_toplevel --- src/handlers/xdg_shell.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index 5e81e44..40a46e5 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -92,6 +92,8 @@ impl XdgShellHandler for State { // TODO: fix it so that reordering this doesn't break stuff self.windows.push(window.clone()); + self.space.map_element(window.clone(), (0, 0), true); + let win_clone = window.clone(); self.schedule( move |_data| { From 8a75769ca3b00f40701f16b1e3630287fecc2e55 Mon Sep 17 00:00:00 2001 From: Ottatop Date: Mon, 4 Sep 2023 02:12:42 -0500 Subject: [PATCH 12/12] Map window if pending state isn't different --- src/handlers/xdg_shell.rs | 17 ++--------------- src/layout.rs | 14 +++++++++++--- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index 40a46e5..162b7e3 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -1,11 +1,8 @@ -use std::time::Duration; - use smithay::{ delegate_xdg_shell, desktop::{ - find_popup_root_surface, layer_map_for_output, utils::surface_primary_scanout_output, - 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, @@ -287,16 +284,6 @@ impl XdgShellHandler for State { // tracing::debug!("acked configure, new loc is {:?}", new_loc); state.loc_request_state = LocationRequestState::Acknowledged(new_loc); - if let Some(focused_output) = - self.focus_state.focused_output.clone() - { - window.send_frame( - &focused_output, - self.clock.now(), - Some(Duration::ZERO), - surface_primary_scanout_output, - ); - } } } Configure::Popup(_) => todo!(), diff --git a/src/layout.rs b/src/layout.rs index 080ee6e..1477b97 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -114,9 +114,17 @@ impl State { window.with_state(|state| { if let LocationRequestState::Sent(loc) = state.loc_request_state { match &window { - WindowElement::Wayland(window) => { - let serial = window.toplevel().send_configure(); - state.loc_request_state = LocationRequestState::Requested(serial, loc); + WindowElement::Wayland(win) => { + // If the above didn't cause any change to size or other state, simply + // map the window. + if !win.toplevel().has_pending_changes() { + state.loc_request_state = LocationRequestState::Idle; + self.space.map_element(window.clone(), loc, false); + } else { + let serial = win.toplevel().send_configure(); + state.loc_request_state = + LocationRequestState::Requested(serial, loc); + } } WindowElement::X11(surface) => { // already configured, just need to map