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 786e5d47..48fa9038 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,7 +86,9 @@ impl ExtImageCopyCaptureFrameV1 { let buffer = self.session.buffer.get().unwrap(); if size != buffer.rect.size() { self.session.buffer_size_changed(); - return Err(FrameFailureReason::BufferConstraints); + // https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/222 + // self.fail(FrameFailureReason::BufferConstraints); + // return; } if let Err(e) = buffer.update_framebuffer() { log::error!("Could not import buffer: {}", ErrorFmt(e)); @@ -100,13 +102,6 @@ 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 @@ -164,12 +159,6 @@ 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); }; @@ -198,11 +187,7 @@ impl ExtImageCopyCaptureFrameV1 { ) { match self.try_copy(on, size, f) { Ok(()) => self.session.status.set(FrameStatus::Captured), - Err(e) => { - if self.session.status.get() != FrameStatus::Failed { - self.fail(e); - } - } + Err(e) => 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 eb7154e7..286e9850 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,11 +83,6 @@ 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 547b7e2a..4224e727 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| { - if pending.damage_full { - let mut damage = pos; + let clip_damage = |mut damage: Rect| { + damage = damage.intersect(pos); if let Some(bounds) = bounds { damage = damage.intersect(bounds); } - self.client.state.damage(damage); + damage + }; + if pending.damage_full { + self.client.state.damage(clip_damage(pos)); } else { let matrix = self.damage_matrix.get(); if let Some(buffer) = self.buffer.get() { for damage in &pending.buffer_damage { - let mut damage = matrix.apply( + let damage = matrix.apply( pos.x1(), pos.y1(), damage.intersect(buffer.buffer.buf.rect), ); - if let Some(bounds) = bounds { - damage = damage.intersect(bounds); - } - self.client.state.damage(damage); + self.client.state.damage(clip_damage(damage)); } } for damage in &pending.surface_damage { @@ -1550,8 +1550,7 @@ impl WlSurface { let y2 = (damage.y2() + scale - 1) / scale; damage = Rect::new_saturating(x1, y1, x2, y2); } - damage = damage.intersect(bounds.unwrap_or(pos)); - self.client.state.damage(damage); + self.client.state.damage(clip_damage(damage)); } } }; diff --git a/src/ifs/zwlr_screencopy_frame_v1.rs b/src/ifs/zwlr_screencopy_frame_v1.rs index d27b9c29..d66035e9 100644 --- a/src/ifs/zwlr_screencopy_frame_v1.rs +++ b/src/ifs/zwlr_screencopy_frame_v1.rs @@ -48,13 +48,16 @@ impl ZwlrScreencopyFrameV1 { } pub fn send_damage(&self) { - self.client.event(Damage { - self_id: self.id, - x: 0, - y: 0, - width: self.rect.width() as _, - height: self.rect.height() as _, - }); + 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 _, + }); + } } pub fn send_buffer(&self) { @@ -108,28 +111,10 @@ impl ZwlrScreencopyFrameV1 { return Err(ZwlrScreencopyFrameV1Error::InvalidBufferFormat); } buffer.update_framebuffer()?; - 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(), - ); - } - _ => {} + if let Some(WlBufferStorage::Shm { stride, .. }) = buffer.storage.borrow_mut().deref() + && *stride != self.rect.width() * 4 + { + return Err(ZwlrScreencopyFrameV1Error::InvalidBufferStride); } self.buffer.set(Some(buffer)); if !with_damage && let Some(global) = self.output.get() { @@ -149,12 +134,6 @@ 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 5f70072f..2ff6c8a4 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 (width, height) = global.pixel_size(); - let mut rect = Rect::new_sized_saturating(0, 0, width, height); + let mode = global.mode.get(); + let mut rect = Rect::new_sized_saturating(0, 0, mode.width, mode.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 b25105c8..e08266de 100644 --- a/src/it/test_ifs/test_viewport.rs +++ b/src/it/test_ifs/test_viewport.rs @@ -29,6 +29,17 @@ 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, @@ -37,6 +48,15 @@ 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 84571c57..28ee359f 100644 --- a/src/it/tests/t0002_window.rs +++ b/src/it/tests/t0002_window.rs @@ -1,7 +1,6 @@ use { crate::{ it::{test_error::TestError, testrun::TestRun}, - rect::Rect, tree::Node, }, std::rc::Rc, @@ -11,29 +10,19 @@ testcase!(); /// Create and map a single surface async fn test(run: Rc) -> Result<(), TestError> { - run.backend.install_default()?; + let ds = run.create_default_setup().await?; let client = run.create_client().await?; let window = client.create_window().await?; window.map().await?; - tassert_eq!(window.tl.core.width.get(), 800); - tassert_eq!( - window.tl.core.height.get(), - 600 - 2 * run.state.theme.title_plus_underline_height() - ); + let workspace_rect = ds.output.workspace_rect.get(); - 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() - ); + 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); Ok(()) } diff --git a/src/it/tests/t0003_multi_window.rs b/src/it/tests/t0003_multi_window.rs index 3fbf599c..db726f90 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> { - run.backend.install_default()?; + let ds = run.create_default_setup().await?; let client = run.create_client().await?; @@ -21,17 +21,30 @@ async fn test(run: Rc) -> Result<(), TestError> { let window2 = client.create_window().await?; window2.map().await?; - let otop = 2 * run.state.theme.title_plus_underline_height(); + let workspace_rect = ds.output.workspace_rect.get(); 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(0, otop, (800 - bw) / 2, 600 - otop).unwrap() + Rect::new_sized( + workspace_rect.x1(), + workspace_rect.y1(), + child_width, + workspace_rect.height(), + ) + .unwrap() ); tassert_eq!( window2.tl.server.node_absolute_position(), - Rect::new_sized((800 - bw) / 2 + bw, otop, (800 - bw) / 2, 600 - otop).unwrap() + Rect::new_sized( + workspace_rect.x1() + child_width + bw, + workspace_rect.y1(), + child_width, + workspace_rect.height(), + ) + .unwrap() ); Ok(()) diff --git a/src/it/tests/t0007_subsurface/screenshot_1.qoi b/src/it/tests/t0007_subsurface/screenshot_1.qoi index 230c0408..b5954651 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 722271f6..718d5c29 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 0186cbaf..dccd1096 100644 --- a/src/it/tests/t0014_container_scroll_focus.rs +++ b/src/it/tests/t0014_container_scroll_focus.rs @@ -48,13 +48,18 @@ async fn test(run: Rc) -> TestResult { let mono_container = w_mono2.tl.container_parent()?; let container_pos = mono_container.tl_data().pos.get(); - 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 _, - ); + 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 _); 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 c6cf49b7..f5cb6e3c 100644 --- a/src/it/tests/t0015_scroll_partial.rs +++ b/src/it/tests/t0015_scroll_partial.rs @@ -26,12 +26,18 @@ async fn test(run: Rc) -> TestResult { let container = w_mono2.tl.container_parent()?; let pos = container.tl_data().pos.get(); - 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, - ); + 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); 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 eef5f37a..4c826f86 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 7e8cf143..0fb763e2 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 1fdacb1a..524856e3 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::TestResult, + test_error::{TestErrorExt, 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, + std::{rc::Rc, time::Duration}, }; testcase!(); @@ -19,6 +19,7 @@ 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); @@ -44,5 +45,23 @@ 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 1fa8d204..960da20a 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 2206fc85..f11111bb 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 f7bf53bf..9f5fca3c 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 b454acd3..aaf1b108 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 dd974ccf..e08dc525 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 f49edd4d..e08dc525 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 b9826001..36c68e4e 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 988bc767..e6f6db74 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 a7509404..9abc8de3 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 8fe5d0b2..80a29c84 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 9874e2f5..735af290 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 d25fcf64..cd07ecd4 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 7f93231a..d76ea9a0 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 d25fcf64..cd07ecd4 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 6423ef6d..6d57d140 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 823fd750..478b3c43 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 823fd750..478b3c43 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 714222f1..07dd87fb 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 d9760bc8..c2d0d6dd 100644 --- a/src/it/tests/t0047_surface_damage.rs +++ b/src/it/tests/t0047_surface_damage.rs @@ -308,9 +308,8 @@ async fn test(run: Rc) -> TestResult { let output_damage = connector_data.damage.borrow(); tassert!(!output_damage.is_empty()); - // 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()); + // The test window maps its 1x1 buffer through a viewport to the full window size. + let expected_buffer_damage = surface_pos; // Find the exact output damage that matches our expected buffer damage let mut found_exact_buffer_damage = false; @@ -331,10 +330,12 @@ 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 - // 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 + // 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(); // Add buffer damage to test viewport scaling coordinate transformation window.surface.damage_buffer(0, 0, 1, 1)?; // Damage entire 1x1 buffer @@ -346,8 +347,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 150x100 - // and be moved by surface position (0, 36) to get output coordinates (0, 36, 150, 136) + // With viewporter scaling, the 1x1 buffer damage should scale to the viewport destination. + let surface_pos = window.surface.server.buffer_abs_pos.get(); 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()); @@ -402,8 +403,9 @@ async fn test(run: Rc) -> TestResult { rotation_window.map().await?; client.sync().await; - // Disable viewporter by setting destination to 0x0 to rely purely on buffer dimensions - rotation_window.surface.viewport.set_destination(0, 0)?; // Disable viewporter + // Disable viewporter to rely purely on buffer dimensions. + rotation_window.surface.viewport.unset_source()?; + rotation_window.surface.viewport.unset_destination()?; // 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 20d21f4a..d3563f0f 100644 --- a/src/portal/ptl_screencast.rs +++ b/src/portal/ptl_screencast.rs @@ -315,14 +315,6 @@ 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()); } @@ -641,18 +633,15 @@ 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; @@ -778,7 +767,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 | WINDOW).0)); + object.set_property::(Variant::U32(MONITOR.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 b81f2e85..44a6a778 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -32,6 +32,7 @@ use { numcell::NumCell, on_drop_event::OnDropEvent, rc_eq::rc_eq, + scroller::Scroller, threshold_counter::ThresholdCounter, }, }, @@ -150,6 +151,7 @@ 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, } @@ -266,6 +268,7 @@ 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), }); @@ -793,6 +796,18 @@ 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() { @@ -1519,7 +1534,7 @@ impl ContainerNode { fn button( self: Rc, id: CursorType, - _seat: &Rc, + seat: &Rc, _time_usec: u64, pressed: bool, button: u32, @@ -1549,7 +1564,7 @@ impl ContainerNode { if let Some(child) = children.get(&child_id) { let child_ref = child.to_ref(); drop(children); - self.activate_child(&child_ref); + self.activate_child_from_input(&child_ref, seat); } return; } @@ -2066,31 +2081,33 @@ 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; } - // Use vertical scroll (index 1) to switch tabs. - let v = match event.v120[1].get() { - Some(v) if v != 0 => v, + let steps = match self.scroll.handle(event) { + Some(steps) => steps, _ => return, }; - let mono = match self.mono_child.get() { + let mut target = match self.mono_child.get() { Some(m) => m, None => return, }; - 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); + 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, } } + 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 440916bf..26b31a88 100644 --- a/src/tree/display.rs +++ b/src/tree/display.rs @@ -8,18 +8,25 @@ use { renderer::Renderer, state::State, tree::{ - FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeLayerLink, NodeLocation, - OutputNode, StackedNode, TileDragDestination, WorkspaceDragDestination, + Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeLayerLink, + NodeLocation, OutputNode, StackedNode, TileDragDestination, WorkspaceDragDestination, WorkspaceNodeId, walker::NodeVisitor, }, utils::{copyhashmap::CopyHashMap, linkedlist::LinkedList}, }, - std::{cell::Cell, ops::Deref, rc::Rc}, + std::{ + cell::{Cell, RefCell}, + mem, + ops::Deref, + rc::{Rc, Weak}, + }, }; 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>>, @@ -31,6 +38,8 @@ 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(), @@ -71,6 +80,17 @@ 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(); } @@ -82,6 +102,20 @@ 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 456e7a5d..d21b3e5e 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -460,15 +460,9 @@ impl OutputNode { } self.lock_surface.take(); self.jay_outputs.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.screencasts.clear(); + self.screencopies.clear(); + self.ext_copy_sessions.clear(); self.ext_workspace_groups.clear(); self.latch_event.clear(); self.vblank_event.clear();