diff --git a/Cargo.toml b/Cargo.toml index 58f9c67..420ad30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ tracing = { git = "https://github.com/tokio-rs/tracing", rev = "b8c45cc" } tracing-subscriber = { git = "https://github.com/tokio-rs/tracing", rev = "b8c45cc", features = ["env-filter"] } tracing-appender = { git = "https://github.com/tokio-rs/tracing", rev = "b8c45cc" } smithay = { git = "https://github.com/Smithay/smithay", rev = "1a61e1c", features = ["desktop", "wayland_frontend"] } -smithay-drm-extras = { git = "https://github.com/Smithay/smithay", optional = true } +smithay-drm-extras = { git = "https://github.com/Smithay/smithay", rev = "1a61e1c", optional = true } thiserror = "1.0.48" xcursor = { version = "0.3.4", optional = true } image = { version = "0.24.7", default-features = false, optional = true } diff --git a/src/backend/udev.rs b/src/backend/udev.rs index e30bb6a..4a5cd3b 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -29,7 +29,10 @@ use smithay::{ libinput::{LibinputInputBackend, LibinputSessionInterface}, renderer::{ damage::{self, OutputDamageTracker}, - element::{texture::TextureBuffer, RenderElement, RenderElementStates}, + element::{ + surface::WaylandSurfaceRenderElement, texture::TextureBuffer, RenderElement, + RenderElementStates, + }, gles::{GlesRenderer, GlesTexture}, multigpu::{gbm::GbmGlesBackend, GpuManager, MultiRenderer, MultiTexture}, sync::SyncPoint, @@ -422,7 +425,7 @@ pub fn run_udev() -> anyhow::Result<()> { } event_loop.run( - Some(Duration::from_millis(1)), + Some(Duration::from_micros(((1.0 / 144.0) * 1000000.0) as u64)), &mut CalloopData { state, display }, |data| { data.state.space.refresh(); @@ -756,6 +759,8 @@ impl State { Ok(()) } + /// A display was plugged in. + // TODO: better edid info from cosmic-comp fn connector_connected( &mut self, node: DrmNode, @@ -969,6 +974,7 @@ impl State { } } + /// A display was unplugged. fn connector_disconnected( &mut self, node: DrmNode, @@ -1036,6 +1042,7 @@ impl State { // crate::shell::fixup_positions(&mut self.space); } + /// A GPU was unplugged. fn device_removed(&mut self, node: DrmNode) { let crtcs = { let Backend::Udev(backend) = &mut self.backend else { @@ -1120,7 +1127,7 @@ impl State { let schedule_render = match surface .compositor .frame_submitted() - .map_err(Into::::into) + .map_err(SwapBuffersError::from) { Ok(user_data) => { if let Some(mut feedback) = user_data { @@ -1243,7 +1250,7 @@ impl State { } } - // If crtc is `Some()`, render it, else render all crtcs + /// Render using the gpu on `node` to the provided `crtc`, or all available crtcs if `None`. fn render(&mut self, node: DrmNode, crtc: Option) { let Backend::Udev(backend) = &mut self.backend else { unreachable!() @@ -1480,7 +1487,16 @@ fn render_surface<'a>( let pending_wins = windows .iter() .filter(|win| win.alive()) - .filter(|win| win.with_state(|state| !state.loc_request_state.is_idle())) + .filter(|win| { + let pending_size = if let WindowElement::Wayland(win) = win { + let current_state = win.toplevel().current_state(); + win.toplevel() + .with_pending_state(|state| state.size != current_state.size) + } else { + false + }; + pending_size || win.with_state(|state| !state.loc_request_state.is_idle()) + }) .map(|win| { ( win.class().unwrap_or("None".to_string()), @@ -1491,13 +1507,14 @@ fn render_surface<'a>( .collect::>(); if !pending_wins.is_empty() { - // tracing::debug!("Skipping frame, waiting on {pending_wins:?}"); + tracing::debug!("Skipping frame, waiting on {pending_wins:?}"); for win in windows.iter() { win.send_frame(output, clock.now(), Some(Duration::ZERO), |_, _| { Some(output.clone()) }); } + // TODO: set waiting for vblank to true here surface .compositor .queue_frame(None, None, None) @@ -1509,14 +1526,14 @@ fn render_surface<'a>( } let output_render_elements = crate::render::generate_render_elements( + output, + renderer, space, windows, override_redirect_windows, pointer_location, cursor_status, dnd_icon, - renderer, - output, input_method, pointer_element, Some(pointer_image), @@ -1552,10 +1569,11 @@ fn render_surface<'a>( if res.rendered { let output_presentation_feedback = take_presentation_feedback(output, space, &res.states); + // TODO: set waiting for vblank to true here surface .compositor .queue_frame(res.sync, res.damage, Some(output_presentation_feedback)) - .map_err(Into::::into)?; + .map_err(SwapBuffersError::from)?; } Ok(res.rendered) @@ -1567,7 +1585,7 @@ fn initial_render( ) -> Result<(), SwapBuffersError> { surface .compositor - .render_frame::<_, CustomRenderElements<_>, GlesTexture>( + .render_frame::<_, CustomRenderElements<_, WaylandSurfaceRenderElement<_>>, GlesTexture>( renderer, &[], [0.6, 0.6, 0.6, 1.0], diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 6ebc9e7..5cfc48b 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -36,6 +36,7 @@ use smithay::{ use crate::{ render::{pointer::PointerElement, take_presentation_feedback}, state::{Backend, CalloopData, State, WithState}, + window::WindowElement, }; use super::BackendData; @@ -241,7 +242,16 @@ pub fn run_winit() -> anyhow::Result<()> { .windows .iter() .filter(|win| win.alive()) - .filter(|win| win.with_state(|state| !state.loc_request_state.is_idle())) + .filter(|win| { + let pending_size = if let WindowElement::Wayland(win) = win { + let current_state = win.toplevel().current_state(); + win.toplevel() + .with_pending_state(|state| state.size != current_state.size) + } else { + false + }; + pending_size || win.with_state(|state| !state.loc_request_state.is_idle()) + }) .map(|win| { ( win.class().unwrap_or("None".to_string()), @@ -253,14 +263,17 @@ pub fn run_winit() -> anyhow::Result<()> { if !pending_wins.is_empty() { // tracing::debug!("Skipping frame, waiting on {pending_wins:?}"); - for win in state.windows.iter() { - win.send_frame( - &output, - state.clock.now(), - Some(Duration::ZERO), - surface_primary_scanout_output, - ); - } + let op_clone = output.clone(); + state.loop_handle.insert_idle(move |dt| { + for win in dt.state.windows.iter() { + win.send_frame( + &op_clone, + dt.state.clock.now(), + Some(Duration::ZERO), + surface_primary_scanout_output, + ); + } + }); state.space.refresh(); state.popup_manager.cleanup(); @@ -282,14 +295,14 @@ pub fn run_winit() -> anyhow::Result<()> { state.focus_state.fix_up_focus(&mut state.space); let output_render_elements = crate::render::generate_render_elements( + &output, + backend.backend.renderer(), &state.space, &state.focus_state.focus_stack, &state.override_redirect_windows, state.pointer_location, &mut state.cursor_status, state.dnd_icon.as_ref(), - backend.backend.renderer(), - &output, state.seat.input_method(), &mut pointer_element, None, @@ -306,7 +319,7 @@ pub fn run_winit() -> anyhow::Result<()> { backend .damage_tracker - .render_output(renderer, age, &output_render_elements, [0.5, 0.5, 0.5, 1.0]) + .render_output(renderer, age, &output_render_elements, [0.6, 0.6, 0.6, 1.0]) .map_err(|err| match err { damage::Error::Rendering(err) => err.into(), damage::Error::OutputNoMode(_) => todo!(), @@ -327,7 +340,7 @@ pub fn run_winit() -> anyhow::Result<()> { let time = state.clock.now(); - // Send frames to the cursor surface so it updates in xwayland + // Send frames to the cursor surface so it updates correctly if let CursorImageStatus::Surface(surf) = &state.cursor_status { if let Some(op) = state.focus_state.focused_output.as_ref() { send_frames_surface_tree( @@ -378,7 +391,7 @@ pub fn run_winit() -> anyhow::Result<()> { .flush_clients() .expect("failed to flush client buffers"); - TimeoutAction::ToDuration(Duration::from_millis(1)) + TimeoutAction::ToDuration(Duration::from_micros(((1.0 / 144.0) * 1000000.0) as u64)) }); if let Err(err) = insert_ret { @@ -386,7 +399,7 @@ pub fn run_winit() -> anyhow::Result<()> { } event_loop.run( - Some(Duration::from_millis(1)), + Some(Duration::from_micros(((1.0 / 144.0) * 1000000.0) as u64)), &mut CalloopData { display, state }, |_data| { // println!("{}", _data.state.space.elements().count()); diff --git a/src/grab/move_grab.rs b/src/grab/move_grab.rs index ae72c30..38e2aaa 100644 --- a/src/grab/move_grab.rs +++ b/src/grab/move_grab.rs @@ -86,6 +86,19 @@ impl PointerGrab for MoveSurfaceGrab { return; } + if state + .space + .element_geometry(&self.window) + .is_some_and(|geo| { + state + .space + .element_geometry(&window_under) + .is_some_and(|geo2| geo.overlaps(geo2)) + }) + { + return; + } + let is_floating = window_under.with_state(|state| state.floating_or_tiled.is_floating()); @@ -101,6 +114,7 @@ impl PointerGrab for MoveSurfaceGrab { return; } + tracing::debug!("Swapping window positions"); state.swap_window_positions(&self.window, &window_under); } } else { diff --git a/src/handlers.rs b/src/handlers.rs index b144a7c..cb35741 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -95,8 +95,7 @@ impl CompositorHandler for State { } fn commit(&mut self, surface: &WlSurface) { - // tracing::debug!("commit on surface {:?}", surface); - + // tracing::debug!("commit on surface {surface:?}"); X11Wm::commit_hook::(surface); utils::on_commit_buffer_handler::(surface); @@ -107,8 +106,16 @@ impl CompositorHandler for State { while let Some(parent) = compositor::get_parent(&root) { root = parent; } - if let Some(WindowElement::Wayland(window)) = self.window_for_surface(surface) { + if let Some(win @ WindowElement::Wayland(window)) = &self.window_for_surface(&root) { + // tracing::debug!("window commit thing {:?}", win.class()); window.on_commit(); + win.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(win.clone(), new_pos, false); + } + }); } }; @@ -118,15 +125,9 @@ impl CompositorHandler for State { crate::grab::resize_grab::handle_commit(self, surface); - 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); - } - }); - } + // if let Some(window) = self.window_for_surface(surface) { + // tracing::debug!("commit on window {:?}", window.class()); + // } } 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 11bbf0f..a24e0d5 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use smithay::{ delegate_xdg_shell, desktop::{ @@ -736,20 +738,30 @@ impl XdgShellHandler for State { fn ack_configure(&mut self, surface: WlSurface, configure: Configure) { if let Some(window) = self.window_for_surface(&surface) { - window.with_state(|state| { - if let LocationRequestState::Requested(serial, new_loc) = state.loc_request_state { - match &configure { - Configure::Toplevel(configure) => { - if configure.serial >= serial { - tracing::debug!("acked configure, new loc is {:?}", new_loc); + if let LocationRequestState::Requested(serial, new_loc) = + window.with_state(|state| state.loc_request_state.clone()) + { + match &configure { + Configure::Toplevel(configure) => { + if configure.serial >= serial { + tracing::debug!("acked configure, new loc is {:?}", new_loc); + window.with_state(|state| { state.loc_request_state = LocationRequestState::Acknowledged(new_loc); + }); + if let Some(op) = window.output(self) { + window.send_frame( + &op, + self.clock.now(), + Some(Duration::ZERO), + |_, _| Some(op.clone()), + ); } } - Configure::Popup(_) => todo!(), } + Configure::Popup(_) => todo!(), } - }); + } } } diff --git a/src/render.rs b/src/render.rs index 59c4b99..c4098bf 100644 --- a/src/render.rs +++ b/src/render.rs @@ -5,8 +5,8 @@ use std::sync::Mutex; use smithay::{ backend::renderer::{ element::{ - self, surface::WaylandSurfaceRenderElement, texture::TextureBuffer, AsRenderElements, - RenderElementStates, Wrap, + self, surface::WaylandSurfaceRenderElement, texture::TextureBuffer, + utils::CropRenderElement, AsRenderElements, RenderElementStates, Wrap, }, ImportAll, ImportMem, Renderer, Texture, }, @@ -38,16 +38,17 @@ use self::pointer::{PointerElement, PointerRenderElement}; pub mod pointer; render_elements! { - pub CustomRenderElements where R: ImportAll + ImportMem; - Pointer=PointerRenderElement, - Surface=WaylandSurfaceRenderElement, + pub CustomRenderElements where R: ImportAll + ImportMem; + Pointer = PointerRenderElement, + Surface = WaylandSurfaceRenderElement, + Crop = CropRenderElement, } render_elements! { pub OutputRenderElements where R: ImportAll + ImportMem; Space=SpaceRenderElements, Window=Wrap, - Custom=CustomRenderElements, + Custom=CustomRenderElements, } impl AsRenderElements for WindowElement @@ -64,7 +65,6 @@ where scale: Scale, alpha: f32, ) -> Vec { - // let window_bbox = self.bbox(); match self { WindowElement::Wayland(window) => { window.render_elements(renderer, location, scale, alpha) @@ -111,7 +111,7 @@ where .map(|(surface, loc)| { let render_elements = surface.render_elements::>( renderer, - loc.to_physical(1), + loc.to_physical((scale.x.round() as i32, scale.x.round() as i32)), scale, 1.0, ); @@ -136,27 +136,35 @@ where } /// Get the render_elements for the provided tags. -fn tag_render_elements( +fn tag_render_elements( windows: &[WindowElement], space: &Space, renderer: &mut R, scale: Scale, -) -> Vec +) -> 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| { + .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, 1.0) + .to_physical((scale.x.round() as i32, scale.x.round() as i32)); + (win.render_elements::>(renderer, loc, scale, 1.0), space.element_geometry(win)) + }).flat_map(|(elems, rect)| { + match rect { + Some(rect) => { + elems.into_iter().filter_map(|elem| { + CropRenderElement::from_element(elem, scale, rect.to_physical_precise_down(scale)) + }).map(CustomRenderElements::from).collect::>() + }, + None => elems.into_iter().map(CustomRenderElements::from).collect(), + } }) .collect::>(); @@ -165,14 +173,14 @@ where #[allow(clippy::too_many_arguments)] pub fn generate_render_elements( + output: &Output, + renderer: &mut R, space: &Space, windows: &[WindowElement], override_redirect_windows: &[X11Surface], pointer_location: Point, cursor_status: &mut CursorImageStatus, dnd_icon: Option<&WlSurface>, - renderer: &mut R, - output: &Output, input_method: &InputMethodHandle, pointer_element: &mut PointerElement, pointer_image: Option<&TextureBuffer>, @@ -187,7 +195,7 @@ where .expect("called output_geometry on an unmapped output"); let scale = Scale::from(output.current_scale().fractional_scale()); - let mut custom_render_elements: Vec> = Vec::new(); + let mut custom_render_elements: Vec> = Vec::new(); // draw input method surface if any let rectangle = input_method.coordinates(); let position = Point::from(( @@ -317,8 +325,7 @@ where overlay, } = layer_render_elements(output, renderer, scale); - let window_render_elements: Vec> = - tag_render_elements(windows, space, renderer, scale); + let window_render_elements = tag_render_elements::(windows, space, renderer, scale); let mut output_render_elements = Vec::>>::new(); @@ -335,8 +342,19 @@ where overlay .into_iter() .chain(top) - .chain(window_render_elements) - .chain(bottom) + .map(CustomRenderElements::from) + .map(OutputRenderElements::from), + ); + + output_render_elements.extend( + window_render_elements + .into_iter() + .map(OutputRenderElements::from), + ); + + output_render_elements.extend( + bottom + .into_iter() .chain(background) .map(CustomRenderElements::from) .map(OutputRenderElements::from),