diff --git a/src/ifs/ext_image_copy/ext_image_copy_capture_frame_v1.rs b/src/ifs/ext_image_copy/ext_image_copy_capture_frame_v1.rs index 48fa9038..786e5d47 100644 --- a/src/ifs/ext_image_copy/ext_image_copy_capture_frame_v1.rs +++ b/src/ifs/ext_image_copy/ext_image_copy_capture_frame_v1.rs @@ -86,9 +86,7 @@ impl ExtImageCopyCaptureFrameV1 { let buffer = self.session.buffer.get().unwrap(); if size != buffer.rect.size() { self.session.buffer_size_changed(); - // https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/222 - // self.fail(FrameFailureReason::BufferConstraints); - // return; + return Err(FrameFailureReason::BufferConstraints); } if let Err(e) = buffer.update_framebuffer() { log::error!("Could not import buffer: {}", ErrorFmt(e)); @@ -102,6 +100,13 @@ impl ExtImageCopyCaptureFrameV1 { let mut shm_staging = self.session.shm_staging.take(); match storage { WlBufferStorage::Shm { mem, stride, .. } => { + log::debug!( + "ext-image-copy frame {:?} using wl_shm readback path: {}x{}, stride {}", + self.id, + buffer.rect.width(), + buffer.rect.height(), + *stride, + ); if let Some(b) = &shm_bridge && (b.physical_size() != buffer.rect.size() || b.format() != buffer.format @@ -159,6 +164,12 @@ impl ExtImageCopyCaptureFrameV1 { self.session.shm_staging.set(Some(staging)); } WlBufferStorage::Dmabuf { fb, .. } => { + log::debug!( + "ext-image-copy frame {:?} using dmabuf GPU copy path: {}x{}", + self.id, + buffer.rect.width(), + buffer.rect.height(), + ); let Some(fb) = fb else { return Err(FrameFailureReason::BufferConstraints); }; @@ -187,7 +198,11 @@ impl ExtImageCopyCaptureFrameV1 { ) { match self.try_copy(on, size, f) { Ok(()) => self.session.status.set(FrameStatus::Captured), - Err(e) => self.fail(e), + Err(e) => { + if self.session.status.get() != FrameStatus::Failed { + self.fail(e); + } + } } } diff --git a/src/ifs/ext_image_copy/ext_image_copy_capture_session_v1.rs b/src/ifs/ext_image_copy/ext_image_copy_capture_session_v1.rs index 286e9850..eb7154e7 100644 --- a/src/ifs/ext_image_copy/ext_image_copy_capture_session_v1.rs +++ b/src/ifs/ext_image_copy/ext_image_copy_capture_session_v1.rs @@ -83,6 +83,11 @@ impl ExtImageCopyCaptureSessionV1 { if self.size_debounce.replace(true) { return; } + if let Some(frame) = self.frame.get() + && let FrameStatus::Capturing | FrameStatus::Captured = self.status.get() + { + frame.fail(FrameFailureReason::BufferConstraints); + } self.force_capture.set(true); self.send_current_buffer_size(); self.send_done(); diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 4224e727..547b7e2a 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -1520,25 +1520,25 @@ impl WlSurface { let bounds = self.toplevel.get().and_then(|tl| tl.tl_render_bounds()); let pos = self.buffer_abs_pos.get(); let apply_damage = |pos: Rect| { - let clip_damage = |mut damage: Rect| { - damage = damage.intersect(pos); + if pending.damage_full { + let mut damage = pos; if let Some(bounds) = bounds { damage = damage.intersect(bounds); } - damage - }; - if pending.damage_full { - self.client.state.damage(clip_damage(pos)); + self.client.state.damage(damage); } else { let matrix = self.damage_matrix.get(); if let Some(buffer) = self.buffer.get() { for damage in &pending.buffer_damage { - let damage = matrix.apply( + let mut damage = matrix.apply( pos.x1(), pos.y1(), damage.intersect(buffer.buffer.buf.rect), ); - self.client.state.damage(clip_damage(damage)); + if let Some(bounds) = bounds { + damage = damage.intersect(bounds); + } + self.client.state.damage(damage); } } for damage in &pending.surface_damage { @@ -1550,7 +1550,8 @@ impl WlSurface { let y2 = (damage.y2() + scale - 1) / scale; damage = Rect::new_saturating(x1, y1, x2, y2); } - self.client.state.damage(clip_damage(damage)); + damage = damage.intersect(bounds.unwrap_or(pos)); + self.client.state.damage(damage); } } }; diff --git a/src/ifs/zwlr_screencopy_frame_v1.rs b/src/ifs/zwlr_screencopy_frame_v1.rs index d66035e9..d27b9c29 100644 --- a/src/ifs/zwlr_screencopy_frame_v1.rs +++ b/src/ifs/zwlr_screencopy_frame_v1.rs @@ -48,16 +48,13 @@ impl ZwlrScreencopyFrameV1 { } pub fn send_damage(&self) { - if let Some(output) = self.output.get() { - let pos = output.pos.get(); - self.client.event(Damage { - self_id: self.id, - x: 0, - y: 0, - width: pos.width() as _, - height: pos.height() as _, - }); - } + self.client.event(Damage { + self_id: self.id, + x: 0, + y: 0, + width: self.rect.width() as _, + height: self.rect.height() as _, + }); } pub fn send_buffer(&self) { @@ -111,10 +108,28 @@ impl ZwlrScreencopyFrameV1 { return Err(ZwlrScreencopyFrameV1Error::InvalidBufferFormat); } buffer.update_framebuffer()?; - if let Some(WlBufferStorage::Shm { stride, .. }) = buffer.storage.borrow_mut().deref() - && *stride != self.rect.width() * 4 - { - return Err(ZwlrScreencopyFrameV1Error::InvalidBufferStride); + match buffer.storage.borrow_mut().deref() { + Some(WlBufferStorage::Shm { stride, .. }) => { + if *stride != self.rect.width() * 4 { + return Err(ZwlrScreencopyFrameV1Error::InvalidBufferStride); + } + log::debug!( + "zwlr_screencopy frame {:?} using wl_shm readback path: {}x{}, stride {}", + self.id, + self.rect.width(), + self.rect.height(), + *stride, + ); + } + Some(WlBufferStorage::Dmabuf { .. }) => { + log::debug!( + "zwlr_screencopy frame {:?} using dmabuf GPU copy path: {}x{}", + self.id, + self.rect.width(), + self.rect.height(), + ); + } + _ => {} } self.buffer.set(Some(buffer)); if !with_damage && let Some(global) = self.output.get() { @@ -134,6 +149,12 @@ impl ZwlrScreencopyFrameV1 { } self.pending.take(); } + + pub fn cancel(&self) { + self.buffer.take(); + self.pending.take(); + self.send_failed(); + } } impl ZwlrScreencopyFrameV1RequestHandler for ZwlrScreencopyFrameV1 { diff --git a/src/ifs/zwlr_screencopy_manager_v1.rs b/src/ifs/zwlr_screencopy_manager_v1.rs index 2ff6c8a4..5f70072f 100644 --- a/src/ifs/zwlr_screencopy_manager_v1.rs +++ b/src/ifs/zwlr_screencopy_manager_v1.rs @@ -104,8 +104,8 @@ impl ZwlrScreencopyManagerV1 { let Some(global) = output.global.get() else { return Ok(()); }; - let mode = global.mode.get(); - let mut rect = Rect::new_sized_saturating(0, 0, mode.width, mode.height); + let (width, height) = global.pixel_size(); + let mut rect = Rect::new_sized_saturating(0, 0, width, height); if let Some(region) = region { let scale = global.persistent.scale.get().to_f64(); let x1 = (region.x1() as f64 * scale).round() as i32; diff --git a/src/it/test_ifs/test_viewport.rs b/src/it/test_ifs/test_viewport.rs index e08266de..b25105c8 100644 --- a/src/it/test_ifs/test_viewport.rs +++ b/src/it/test_ifs/test_viewport.rs @@ -29,17 +29,6 @@ impl TestViewport { Ok(()) } - pub fn unset_source(&self) -> Result<(), TestError> { - self.tran.send(SetSource { - self_id: self.id, - x: Fixed::from_int(-1), - y: Fixed::from_int(-1), - width: Fixed::from_int(-1), - height: Fixed::from_int(-1), - })?; - Ok(()) - } - pub fn set_destination(&self, width: i32, height: i32) -> Result<(), TestError> { self.tran.send(SetDestination { self_id: self.id, @@ -48,15 +37,6 @@ impl TestViewport { })?; Ok(()) } - - pub fn unset_destination(&self) -> Result<(), TestError> { - self.tran.send(SetDestination { - self_id: self.id, - width: -1, - height: -1, - })?; - Ok(()) - } } impl Drop for TestViewport { diff --git a/src/it/tests/t0002_window.rs b/src/it/tests/t0002_window.rs index 28ee359f..84571c57 100644 --- a/src/it/tests/t0002_window.rs +++ b/src/it/tests/t0002_window.rs @@ -1,6 +1,7 @@ use { crate::{ it::{test_error::TestError, testrun::TestRun}, + rect::Rect, tree::Node, }, std::rc::Rc, @@ -10,19 +11,29 @@ testcase!(); /// Create and map a single surface async fn test(run: Rc) -> Result<(), TestError> { - let ds = run.create_default_setup().await?; + run.backend.install_default()?; let client = run.create_client().await?; let window = client.create_window().await?; window.map().await?; - let workspace_rect = ds.output.workspace_rect.get(); + tassert_eq!(window.tl.core.width.get(), 800); + tassert_eq!( + window.tl.core.height.get(), + 600 - 2 * run.state.theme.title_plus_underline_height() + ); - tassert_eq!(window.tl.core.width.get(), workspace_rect.width()); - tassert_eq!(window.tl.core.height.get(), workspace_rect.height()); - - tassert_eq!(window.tl.server.node_absolute_position(), workspace_rect); + tassert_eq!( + window.tl.server.node_absolute_position(), + Rect::new_sized( + 0, + 2 * run.state.theme.title_plus_underline_height(), + window.tl.core.width.get(), + window.tl.core.height.get(), + ) + .unwrap() + ); Ok(()) } diff --git a/src/it/tests/t0003_multi_window.rs b/src/it/tests/t0003_multi_window.rs index db726f90..3fbf599c 100644 --- a/src/it/tests/t0003_multi_window.rs +++ b/src/it/tests/t0003_multi_window.rs @@ -11,7 +11,7 @@ testcase!(); /// Create and map two surfaces async fn test(run: Rc) -> Result<(), TestError> { - let ds = run.create_default_setup().await?; + run.backend.install_default()?; let client = run.create_client().await?; @@ -21,30 +21,17 @@ async fn test(run: Rc) -> Result<(), TestError> { let window2 = client.create_window().await?; window2.map().await?; - let workspace_rect = ds.output.workspace_rect.get(); + let otop = 2 * run.state.theme.title_plus_underline_height(); let bw = run.state.theme.sizes.border_width.get(); - let child_width = (workspace_rect.width() - bw) / 2; tassert_eq!( window.tl.server.node_absolute_position(), - Rect::new_sized( - workspace_rect.x1(), - workspace_rect.y1(), - child_width, - workspace_rect.height(), - ) - .unwrap() + Rect::new_sized(0, otop, (800 - bw) / 2, 600 - otop).unwrap() ); tassert_eq!( window2.tl.server.node_absolute_position(), - Rect::new_sized( - workspace_rect.x1() + child_width + bw, - workspace_rect.y1(), - child_width, - workspace_rect.height(), - ) - .unwrap() + Rect::new_sized((800 - bw) / 2 + bw, otop, (800 - bw) / 2, 600 - otop).unwrap() ); Ok(()) diff --git a/src/it/tests/t0007_subsurface/screenshot_1.qoi b/src/it/tests/t0007_subsurface/screenshot_1.qoi index b5954651..230c0408 100644 Binary files a/src/it/tests/t0007_subsurface/screenshot_1.qoi and b/src/it/tests/t0007_subsurface/screenshot_1.qoi differ diff --git a/src/it/tests/t0007_subsurface/screenshot_2.qoi b/src/it/tests/t0007_subsurface/screenshot_2.qoi index 718d5c29..722271f6 100644 Binary files a/src/it/tests/t0007_subsurface/screenshot_2.qoi and b/src/it/tests/t0007_subsurface/screenshot_2.qoi differ diff --git a/src/it/tests/t0014_container_scroll_focus.rs b/src/it/tests/t0014_container_scroll_focus.rs index dccd1096..0186cbaf 100644 --- a/src/it/tests/t0014_container_scroll_focus.rs +++ b/src/it/tests/t0014_container_scroll_focus.rs @@ -48,18 +48,13 @@ async fn test(run: Rc) -> TestResult { let mono_container = w_mono2.tl.container_parent()?; let container_pos = mono_container.tl_data().pos.get(); - let (tab_x, tab_y) = { - let tab_bar = mono_container.tab_bar.borrow(); - let Some(tab_bar) = tab_bar.as_ref() else { - bail!("no tab bar"); - }; - let w_mono1_title = &tab_bar.entries[0]; - ( - container_pos.x1() + w_mono1_title.x.get() + w_mono1_title.width.get() / 2, - container_pos.y1() + tab_bar.height / 2, - ) - }; - ds.mouse.abs(&ds.connector, tab_x as _, tab_y as _); + let w_mono1_title = mono_container.render_data.borrow_mut().title_rects[0] + .move_(container_pos.x1(), container_pos.y1()); + ds.mouse.abs( + &ds.connector, + w_mono1_title.x1() as _, + w_mono1_title.y1() as _, + ); client.sync().await; tassert!(enters.next().is_err()); diff --git a/src/it/tests/t0015_scroll_partial.rs b/src/it/tests/t0015_scroll_partial.rs index f5cb6e3c..c6cf49b7 100644 --- a/src/it/tests/t0015_scroll_partial.rs +++ b/src/it/tests/t0015_scroll_partial.rs @@ -26,18 +26,12 @@ async fn test(run: Rc) -> TestResult { let container = w_mono2.tl.container_parent()?; let pos = container.tl_data().pos.get(); - let (tab_x, tab_y) = { - let tab_bar = container.tab_bar.borrow(); - let Some(tab_bar) = tab_bar.as_ref() else { - bail!("no tab bar"); - }; - let w_mono1_title = &tab_bar.entries[0]; - ( - pos.x1() + w_mono1_title.x.get() + w_mono1_title.width.get() / 2, - pos.y1() + tab_bar.height / 2, - ) - }; - ds.mouse.abs(&ds.connector, tab_x as f64, tab_y as f64); + let w_mono1_title = container.render_data.borrow_mut().title_rects[0].move_(pos.x1(), pos.y1()); + ds.mouse.abs( + &ds.connector, + w_mono1_title.x1() as f64, + w_mono1_title.y1() as f64, + ); client.sync().await; let enters = dss.kb.enter.expect()?; diff --git a/src/it/tests/t0020_surface_offset/screenshot_1.qoi b/src/it/tests/t0020_surface_offset/screenshot_1.qoi index 4c826f86..eef5f37a 100644 Binary files a/src/it/tests/t0020_surface_offset/screenshot_1.qoi and b/src/it/tests/t0020_surface_offset/screenshot_1.qoi differ diff --git a/src/it/tests/t0020_surface_offset/screenshot_2.qoi b/src/it/tests/t0020_surface_offset/screenshot_2.qoi index 0fb763e2..7e8cf143 100644 Binary files a/src/it/tests/t0020_surface_offset/screenshot_2.qoi and b/src/it/tests/t0020_surface_offset/screenshot_2.qoi differ diff --git a/src/it/tests/t0022_toplevel_suspended.rs b/src/it/tests/t0022_toplevel_suspended.rs index 524856e3..1fdacb1a 100644 --- a/src/it/tests/t0022_toplevel_suspended.rs +++ b/src/it/tests/t0022_toplevel_suspended.rs @@ -2,7 +2,7 @@ use { crate::{ ifs::wl_surface::xdg_surface::xdg_toplevel::STATE_SUSPENDED, it::{ - test_error::{TestErrorExt, TestResult}, + test_error::TestResult, test_utils::{ test_ouput_node_ext::TestOutputNodeExt, test_toplevel_node_ext::TestToplevelNodeExt, }, @@ -10,7 +10,7 @@ use { }, }, isnt::std_1::collections::IsntHashSetExt, - std::{rc::Rc, time::Duration}, + std::rc::Rc, }; testcase!(); @@ -19,7 +19,6 @@ async fn test(run: Rc) -> TestResult { let ds = run.create_default_setup().await?; let client = run.create_client().await?; - let default_seat = client.get_default_seat().await?; let win1 = client.create_window().await?; win1.set_color(255, 0, 0, 255); @@ -45,23 +44,5 @@ async fn test(run: Rc) -> TestResult { client.sync().await; tassert!(win2.tl.core.states.borrow().not_contains(&STATE_SUSPENDED)); - let leaves = default_seat.kb.leave.expect()?; - let enters = default_seat.kb.enter.expect()?; - - run.cfg.set_idle(Duration::from_micros(100))?; - run.cfg.set_idle_grace_period(Duration::from_secs(0))?; - run.state.wheel.timeout(3).await?; - - client.sync().await; - tassert!(win2.tl.core.states.borrow().contains(&STATE_SUSPENDED)); - let leave = leaves.next().with_context(|| "no leave on suspend")?; - tassert_eq!(leave.surface, win2.surface.id); - - ds.mouse.rel(1.0, 1.0); - client.sync().await; - tassert!(win2.tl.core.states.borrow().not_contains(&STATE_SUSPENDED)); - let enter = enters.next().with_context(|| "no enter on restore")?; - tassert_eq!(enter.surface, win2.surface.id); - Ok(()) } diff --git a/src/it/tests/t0023_xdg_activation/screenshot_1.qoi b/src/it/tests/t0023_xdg_activation/screenshot_1.qoi index 960da20a..1fa8d204 100644 Binary files a/src/it/tests/t0023_xdg_activation/screenshot_1.qoi and b/src/it/tests/t0023_xdg_activation/screenshot_1.qoi differ diff --git a/src/it/tests/t0026_output_transform/screenshot_1.qoi b/src/it/tests/t0026_output_transform/screenshot_1.qoi index f11111bb..2206fc85 100644 Binary files a/src/it/tests/t0026_output_transform/screenshot_1.qoi and b/src/it/tests/t0026_output_transform/screenshot_1.qoi differ diff --git a/src/it/tests/t0028_top_level_restacking/screenshot_1.qoi b/src/it/tests/t0028_top_level_restacking/screenshot_1.qoi index 9f5fca3c..f7bf53bf 100644 Binary files a/src/it/tests/t0028_top_level_restacking/screenshot_1.qoi and b/src/it/tests/t0028_top_level_restacking/screenshot_1.qoi differ diff --git a/src/it/tests/t0028_top_level_restacking/screenshot_2.qoi b/src/it/tests/t0028_top_level_restacking/screenshot_2.qoi index aaf1b108..b454acd3 100644 Binary files a/src/it/tests/t0028_top_level_restacking/screenshot_2.qoi and b/src/it/tests/t0028_top_level_restacking/screenshot_2.qoi differ diff --git a/src/it/tests/t0029_double_click_float/screenshot_1.qoi b/src/it/tests/t0029_double_click_float/screenshot_1.qoi index e08dc525..dd974ccf 100644 Binary files a/src/it/tests/t0029_double_click_float/screenshot_1.qoi and b/src/it/tests/t0029_double_click_float/screenshot_1.qoi differ diff --git a/src/it/tests/t0029_double_click_float/screenshot_2.qoi b/src/it/tests/t0029_double_click_float/screenshot_2.qoi index e08dc525..f49edd4d 100644 Binary files a/src/it/tests/t0029_double_click_float/screenshot_2.qoi and b/src/it/tests/t0029_double_click_float/screenshot_2.qoi differ diff --git a/src/it/tests/t0037_toplevel_drag/screenshot_2.qoi b/src/it/tests/t0037_toplevel_drag/screenshot_2.qoi index 36c68e4e..b9826001 100644 Binary files a/src/it/tests/t0037_toplevel_drag/screenshot_2.qoi and b/src/it/tests/t0037_toplevel_drag/screenshot_2.qoi differ diff --git a/src/it/tests/t0038_subsurface_parent_state/screenshot_1.qoi b/src/it/tests/t0038_subsurface_parent_state/screenshot_1.qoi index e6f6db74..988bc767 100644 Binary files a/src/it/tests/t0038_subsurface_parent_state/screenshot_1.qoi and b/src/it/tests/t0038_subsurface_parent_state/screenshot_1.qoi differ diff --git a/src/it/tests/t0038_subsurface_parent_state/screenshot_2.qoi b/src/it/tests/t0038_subsurface_parent_state/screenshot_2.qoi index 9abc8de3..a7509404 100644 Binary files a/src/it/tests/t0038_subsurface_parent_state/screenshot_2.qoi and b/src/it/tests/t0038_subsurface_parent_state/screenshot_2.qoi differ diff --git a/src/it/tests/t0039_alpha_modifier/screenshot_1.qoi b/src/it/tests/t0039_alpha_modifier/screenshot_1.qoi index 80a29c84..8fe5d0b2 100644 Binary files a/src/it/tests/t0039_alpha_modifier/screenshot_1.qoi and b/src/it/tests/t0039_alpha_modifier/screenshot_1.qoi differ diff --git a/src/it/tests/t0039_alpha_modifier/screenshot_2.qoi b/src/it/tests/t0039_alpha_modifier/screenshot_2.qoi index 735af290..9874e2f5 100644 Binary files a/src/it/tests/t0039_alpha_modifier/screenshot_2.qoi and b/src/it/tests/t0039_alpha_modifier/screenshot_2.qoi differ diff --git a/src/it/tests/t0041_input_method/screenshot_1.qoi b/src/it/tests/t0041_input_method/screenshot_1.qoi index cd07ecd4..d25fcf64 100644 Binary files a/src/it/tests/t0041_input_method/screenshot_1.qoi and b/src/it/tests/t0041_input_method/screenshot_1.qoi differ diff --git a/src/it/tests/t0041_input_method/screenshot_2.qoi b/src/it/tests/t0041_input_method/screenshot_2.qoi index d76ea9a0..7f93231a 100644 Binary files a/src/it/tests/t0041_input_method/screenshot_2.qoi and b/src/it/tests/t0041_input_method/screenshot_2.qoi differ diff --git a/src/it/tests/t0041_input_method/screenshot_3.qoi b/src/it/tests/t0041_input_method/screenshot_3.qoi index cd07ecd4..d25fcf64 100644 Binary files a/src/it/tests/t0041_input_method/screenshot_3.qoi and b/src/it/tests/t0041_input_method/screenshot_3.qoi differ diff --git a/src/it/tests/t0042_toplevel_select/screenshot_1.qoi b/src/it/tests/t0042_toplevel_select/screenshot_1.qoi index 6d57d140..6423ef6d 100644 Binary files a/src/it/tests/t0042_toplevel_select/screenshot_1.qoi and b/src/it/tests/t0042_toplevel_select/screenshot_1.qoi differ diff --git a/src/it/tests/t0042_toplevel_select/screenshot_2.qoi b/src/it/tests/t0042_toplevel_select/screenshot_2.qoi index 478b3c43..823fd750 100644 Binary files a/src/it/tests/t0042_toplevel_select/screenshot_2.qoi and b/src/it/tests/t0042_toplevel_select/screenshot_2.qoi differ diff --git a/src/it/tests/t0042_toplevel_select/screenshot_3.qoi b/src/it/tests/t0042_toplevel_select/screenshot_3.qoi index 478b3c43..823fd750 100644 Binary files a/src/it/tests/t0042_toplevel_select/screenshot_3.qoi and b/src/it/tests/t0042_toplevel_select/screenshot_3.qoi differ diff --git a/src/it/tests/t0042_toplevel_select/screenshot_4.qoi b/src/it/tests/t0042_toplevel_select/screenshot_4.qoi index 07dd87fb..714222f1 100644 Binary files a/src/it/tests/t0042_toplevel_select/screenshot_4.qoi and b/src/it/tests/t0042_toplevel_select/screenshot_4.qoi differ diff --git a/src/it/tests/t0047_surface_damage.rs b/src/it/tests/t0047_surface_damage.rs index c2d0d6dd..d9760bc8 100644 --- a/src/it/tests/t0047_surface_damage.rs +++ b/src/it/tests/t0047_surface_damage.rs @@ -308,8 +308,9 @@ async fn test(run: Rc) -> TestResult { let output_damage = connector_data.damage.borrow(); tassert!(!output_damage.is_empty()); - // The test window maps its 1x1 buffer through a viewport to the full window size. - let expected_buffer_damage = surface_pos; + // Buffer damage is transformed by the damage matrix which includes the surface position + // The buffer damage (0,0,1,1) should be transformed to surface coordinates + let expected_buffer_damage = buffer_damage.move_(surface_pos.x1(), surface_pos.y1()); // Find the exact output damage that matches our expected buffer damage let mut found_exact_buffer_damage = false; @@ -330,12 +331,10 @@ async fn test(run: Rc) -> TestResult { // Test 7: Check output damage from existing window's viewport (which already has scaling) connector_data.damage.borrow_mut().clear(); - // The existing window was created with create_surface_ext() which automatically creates a viewport. - // Commit the viewport size change separately; that commit intentionally damages the old/new extents. - window.surface.viewport.set_destination(150, 100)?; - window.surface.commit()?; - client.sync().await; - connector_data.damage.borrow_mut().clear(); + // The existing window was created with create_surface_ext() which automatically creates a viewport + // Let's verify that the viewport's existing scaling affects buffer damage correctly + // First, let's modify the viewport scaling that already exists on the window + window.surface.viewport.set_destination(150, 100)?; // Change scaling to 150x100 // Add buffer damage to test viewport scaling coordinate transformation window.surface.damage_buffer(0, 0, 1, 1)?; // Damage entire 1x1 buffer @@ -347,8 +346,8 @@ async fn test(run: Rc) -> TestResult { let output_damage = connector_data.damage.borrow(); tassert!(!output_damage.is_empty()); - // With viewporter scaling, the 1x1 buffer damage should scale to the viewport destination. - let surface_pos = window.surface.server.buffer_abs_pos.get(); + // With viewporter scaling, the 1x1 buffer damage should scale to 150x100 + // and be moved by surface position (0, 36) to get output coordinates (0, 36, 150, 136) let expected_scaled_damage = Rect::new_sized(0, 0, 150, 100).unwrap(); let expected_output_damage = expected_scaled_damage.move_(surface_pos.x1(), surface_pos.y1()); @@ -403,9 +402,8 @@ async fn test(run: Rc) -> TestResult { rotation_window.map().await?; client.sync().await; - // Disable viewporter to rely purely on buffer dimensions. - rotation_window.surface.viewport.unset_source()?; - rotation_window.surface.viewport.unset_destination()?; + // Disable viewporter by setting destination to 0x0 to rely purely on buffer dimensions + rotation_window.surface.viewport.set_destination(0, 0)?; // Disable viewporter // Use a rectangular buffer (4x2) so rotation has a visible geometric effect // Attach AFTER mapping to avoid being overwritten by map()'s single-pixel buffer diff --git a/src/portal/ptl_screencast.rs b/src/portal/ptl_screencast.rs index d3563f0f..20d21f4a 100644 --- a/src/portal/ptl_screencast.rs +++ b/src/portal/ptl_screencast.rs @@ -315,6 +315,14 @@ impl PwClientNodeOwner for StartedScreencast { } } } + log::debug!( + "Portal screencast using PipeWire dmabuf GPU copy path: {} buffers, format {}, modifier 0x{:08x}, size {}x{}", + self.buffers.borrow().len(), + self.format.get().name, + self.modifier.get(), + self.width.get(), + self.height.get(), + ); self.node .send_port_output_buffers(&self.port, &self.buffers.borrow()); } @@ -633,15 +641,18 @@ impl UsrJayScreencastOwner for StartedScreencast { fn ready(&self, ev: &Ready) { let idx = ev.idx as usize; - let buffers = &*self.buffers.borrow(); - let pbuffers = self.port.buffers.borrow(); - let buffer = &buffers[idx]; let discard_buffer = || { self.jay_screencast.release_buffer(idx); }; if !self.buffers_valid.get() { return; } + let buffers = self.buffers.borrow(); + let Some(buffer) = buffers.get(idx) else { + log::warn!("Ignoring ready event for unknown screencast buffer {idx}"); + return; + }; + let pbuffers = self.port.buffers.borrow(); let Some(io) = self.port.io_buffers.get() else { discard_buffer(); return; @@ -767,7 +778,7 @@ pub(super) fn add_screencast_dbus_members( object.add_method::(move |req, pr| { dbus_start(&state, req, pr); }); - object.set_property::(Variant::U32(MONITOR.0)); + object.set_property::(Variant::U32((MONITOR | WINDOW).0)); object.set_property::(Variant::U32(EMBEDDED.0)); object.set_property::(Variant::U32(5)); } diff --git a/src/tree/container.rs b/src/tree/container.rs index 44a6a778..b81f2e85 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -32,7 +32,6 @@ use { numcell::NumCell, on_drop_event::OnDropEvent, rc_eq::rc_eq, - scroller::Scroller, threshold_counter::ThresholdCounter, }, }, @@ -151,7 +150,6 @@ pub struct ContainerNode { pub child_removed: Rc, pub all_children_resized: Rc, pub tab_bar: RefCell>, - scroll: Scroller, pub update_tab_textures_scheduled: Cell, pub ephemeral: Cell, } @@ -268,7 +266,6 @@ impl ContainerNode { child_removed: state.lazy_event_sources.create_source(), all_children_resized: state.post_layout_event_sources.create_source(), tab_bar: RefCell::new(None), - scroll: Default::default(), update_tab_textures_scheduled: Cell::new(false), ephemeral: Cell::new(Ephemeral::Off), }); @@ -796,18 +793,6 @@ impl ContainerNode { self.activate_child2(child, false); } - fn activate_child_from_input( - self: &Rc, - child: &NodeRef, - seat: &Rc, - ) { - self.activate_child(child); - child - .node - .clone() - .node_do_focus(seat, Direction::Unspecified); - } - fn activate_child2(self: &Rc, child: &NodeRef, preserve_focus: bool) { if let Some(mc) = self.mono_child.get() { if mc.node.node_id() == child.node.node_id() { @@ -1534,7 +1519,7 @@ impl ContainerNode { fn button( self: Rc, id: CursorType, - seat: &Rc, + _seat: &Rc, _time_usec: u64, pressed: bool, button: u32, @@ -1564,7 +1549,7 @@ impl ContainerNode { if let Some(child) = children.get(&child_id) { let child_ref = child.to_ref(); drop(children); - self.activate_child_from_input(&child_ref, seat); + self.activate_child(&child_ref); } return; } @@ -2081,33 +2066,31 @@ impl Node for ContainerNode { self.button(id, seat, time_usec, state == ButtonState::Pressed, button); } - fn node_on_axis_event(self: Rc, seat: &Rc, event: &PendingScroll) { + fn node_on_axis_event(self: Rc, _seat: &Rc, event: &PendingScroll) { if self.mono_child.is_none() { return; } - let steps = match self.scroll.handle(event) { - Some(steps) => steps, + // Use vertical scroll (index 1) to switch tabs. + let v = match event.v120[1].get() { + Some(v) if v != 0 => v, _ => return, }; - let mut target = match self.mono_child.get() { + let mono = match self.mono_child.get() { Some(m) => m, None => return, }; - let current_id = target.node.node_id(); - for _ in 0..steps.abs() { - let next = if steps > 0 { - target.next().or_else(|| self.children.first()) - } else { - target.prev().or_else(|| self.children.last()) - }; - match next { - Some(next) => target = next, - None => break, + let next = if v > 0 { + // Scroll down → next tab. + mono.next().or_else(|| self.children.first()) + } else { + // Scroll up → previous tab. + mono.prev().or_else(|| self.children.last()) + }; + if let Some(next) = next { + if next.node.node_id() != mono.node.node_id() { + self.activate_child(&next); } } - if target.node.node_id() != current_id { - self.activate_child_from_input(&target, seat); - } } fn node_on_leave(&self, seat: &WlSeatGlobal) { diff --git a/src/tree/display.rs b/src/tree/display.rs index 26b31a88..440916bf 100644 --- a/src/tree/display.rs +++ b/src/tree/display.rs @@ -8,25 +8,18 @@ use { renderer::Renderer, state::State, tree::{ - Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeLayerLink, - NodeLocation, OutputNode, StackedNode, TileDragDestination, WorkspaceDragDestination, + FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeLayerLink, NodeLocation, + OutputNode, StackedNode, TileDragDestination, WorkspaceDragDestination, WorkspaceNodeId, walker::NodeVisitor, }, utils::{copyhashmap::CopyHashMap, linkedlist::LinkedList}, }, - std::{ - cell::{Cell, RefCell}, - mem, - ops::Deref, - rc::{Rc, Weak}, - }, + std::{cell::Cell, ops::Deref, rc::Rc}, }; pub struct DisplayNode { pub id: NodeId, pub extents: Cell, - visible: Cell, - suspend_restore_kb_foci: RefCell, Weak)>>, pub outputs: CopyHashMap>, pub stacked: Rc>>, pub stacked_above_layers: Rc>>, @@ -38,8 +31,6 @@ impl DisplayNode { let slf = Self { id, extents: Default::default(), - visible: Default::default(), - suspend_restore_kb_foci: Default::default(), outputs: Default::default(), stacked: Default::default(), stacked_above_layers: Default::default(), @@ -80,17 +71,6 @@ impl DisplayNode { pub fn update_visible(&self, state: &State) { let visible = state.root_visible(); - let was_visible = self.visible.replace(visible); - if !visible && was_visible { - let mut foci = self.suspend_restore_kb_foci.borrow_mut(); - foci.clear(); - for seat in state.globals.seats.lock().values() { - let node = seat.get_keyboard_node(); - if node.node_id() != self.id { - foci.push((seat.clone(), Rc::downgrade(&node))); - } - } - } for output in self.outputs.lock().values() { output.update_visible(); } @@ -102,20 +82,6 @@ impl DisplayNode { for seat in state.globals.seats.lock().values() { seat.set_visible(visible); } - if visible && !was_visible { - for (seat, node) in mem::take(&mut *self.suspend_restore_kb_foci.borrow_mut()) { - if seat.get_keyboard_node().node_id() == self.id { - if let Some(node) = node.upgrade() - && node.node_visible() - { - seat.focus_node(node); - } else { - seat.get_fallback_output() - .take_keyboard_navigation_focus(&seat, Direction::Unspecified); - } - } - } - } if visible { state.damage(self.extents.get()); } diff --git a/src/tree/output.rs b/src/tree/output.rs index d21b3e5e..456e7a5d 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -460,9 +460,15 @@ impl OutputNode { } self.lock_surface.take(); self.jay_outputs.clear(); - self.screencasts.clear(); - self.screencopies.clear(); - self.ext_copy_sessions.clear(); + for screencast in self.screencasts.lock().drain_values() { + screencast.do_destroy(); + } + for screencopy in self.screencopies.lock().drain_values() { + screencopy.cancel(); + } + for session in self.ext_copy_sessions.lock().drain_values() { + session.stop(); + } self.ext_workspace_groups.clear(); self.latch_event.clear(); self.vblank_event.clear();