Compare commits
1 commit
master
...
feat/scree
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b2550d567 |
38 changed files with 162 additions and 208 deletions
|
|
@ -86,9 +86,7 @@ impl ExtImageCopyCaptureFrameV1 {
|
||||||
let buffer = self.session.buffer.get().unwrap();
|
let buffer = self.session.buffer.get().unwrap();
|
||||||
if size != buffer.rect.size() {
|
if size != buffer.rect.size() {
|
||||||
self.session.buffer_size_changed();
|
self.session.buffer_size_changed();
|
||||||
// https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/222
|
return Err(FrameFailureReason::BufferConstraints);
|
||||||
// self.fail(FrameFailureReason::BufferConstraints);
|
|
||||||
// return;
|
|
||||||
}
|
}
|
||||||
if let Err(e) = buffer.update_framebuffer() {
|
if let Err(e) = buffer.update_framebuffer() {
|
||||||
log::error!("Could not import buffer: {}", ErrorFmt(e));
|
log::error!("Could not import buffer: {}", ErrorFmt(e));
|
||||||
|
|
@ -102,6 +100,13 @@ impl ExtImageCopyCaptureFrameV1 {
|
||||||
let mut shm_staging = self.session.shm_staging.take();
|
let mut shm_staging = self.session.shm_staging.take();
|
||||||
match storage {
|
match storage {
|
||||||
WlBufferStorage::Shm { mem, stride, .. } => {
|
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
|
if let Some(b) = &shm_bridge
|
||||||
&& (b.physical_size() != buffer.rect.size()
|
&& (b.physical_size() != buffer.rect.size()
|
||||||
|| b.format() != buffer.format
|
|| b.format() != buffer.format
|
||||||
|
|
@ -159,6 +164,12 @@ impl ExtImageCopyCaptureFrameV1 {
|
||||||
self.session.shm_staging.set(Some(staging));
|
self.session.shm_staging.set(Some(staging));
|
||||||
}
|
}
|
||||||
WlBufferStorage::Dmabuf { fb, .. } => {
|
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 {
|
let Some(fb) = fb else {
|
||||||
return Err(FrameFailureReason::BufferConstraints);
|
return Err(FrameFailureReason::BufferConstraints);
|
||||||
};
|
};
|
||||||
|
|
@ -187,7 +198,11 @@ impl ExtImageCopyCaptureFrameV1 {
|
||||||
) {
|
) {
|
||||||
match self.try_copy(on, size, f) {
|
match self.try_copy(on, size, f) {
|
||||||
Ok(()) => self.session.status.set(FrameStatus::Captured),
|
Ok(()) => self.session.status.set(FrameStatus::Captured),
|
||||||
Err(e) => self.fail(e),
|
Err(e) => {
|
||||||
|
if self.session.status.get() != FrameStatus::Failed {
|
||||||
|
self.fail(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,11 @@ impl ExtImageCopyCaptureSessionV1 {
|
||||||
if self.size_debounce.replace(true) {
|
if self.size_debounce.replace(true) {
|
||||||
return;
|
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.force_capture.set(true);
|
||||||
self.send_current_buffer_size();
|
self.send_current_buffer_size();
|
||||||
self.send_done();
|
self.send_done();
|
||||||
|
|
|
||||||
|
|
@ -1520,25 +1520,25 @@ impl WlSurface {
|
||||||
let bounds = self.toplevel.get().and_then(|tl| tl.tl_render_bounds());
|
let bounds = self.toplevel.get().and_then(|tl| tl.tl_render_bounds());
|
||||||
let pos = self.buffer_abs_pos.get();
|
let pos = self.buffer_abs_pos.get();
|
||||||
let apply_damage = |pos: Rect| {
|
let apply_damage = |pos: Rect| {
|
||||||
let clip_damage = |mut damage: Rect| {
|
if pending.damage_full {
|
||||||
damage = damage.intersect(pos);
|
let mut damage = pos;
|
||||||
if let Some(bounds) = bounds {
|
if let Some(bounds) = bounds {
|
||||||
damage = damage.intersect(bounds);
|
damage = damage.intersect(bounds);
|
||||||
}
|
}
|
||||||
damage
|
self.client.state.damage(damage);
|
||||||
};
|
|
||||||
if pending.damage_full {
|
|
||||||
self.client.state.damage(clip_damage(pos));
|
|
||||||
} else {
|
} else {
|
||||||
let matrix = self.damage_matrix.get();
|
let matrix = self.damage_matrix.get();
|
||||||
if let Some(buffer) = self.buffer.get() {
|
if let Some(buffer) = self.buffer.get() {
|
||||||
for damage in &pending.buffer_damage {
|
for damage in &pending.buffer_damage {
|
||||||
let damage = matrix.apply(
|
let mut damage = matrix.apply(
|
||||||
pos.x1(),
|
pos.x1(),
|
||||||
pos.y1(),
|
pos.y1(),
|
||||||
damage.intersect(buffer.buffer.buf.rect),
|
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 {
|
for damage in &pending.surface_damage {
|
||||||
|
|
@ -1550,7 +1550,8 @@ impl WlSurface {
|
||||||
let y2 = (damage.y2() + scale - 1) / scale;
|
let y2 = (damage.y2() + scale - 1) / scale;
|
||||||
damage = Rect::new_saturating(x1, y1, x2, y2);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -48,16 +48,13 @@ impl ZwlrScreencopyFrameV1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_damage(&self) {
|
pub fn send_damage(&self) {
|
||||||
if let Some(output) = self.output.get() {
|
self.client.event(Damage {
|
||||||
let pos = output.pos.get();
|
self_id: self.id,
|
||||||
self.client.event(Damage {
|
x: 0,
|
||||||
self_id: self.id,
|
y: 0,
|
||||||
x: 0,
|
width: self.rect.width() as _,
|
||||||
y: 0,
|
height: self.rect.height() as _,
|
||||||
width: pos.width() as _,
|
});
|
||||||
height: pos.height() as _,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_buffer(&self) {
|
pub fn send_buffer(&self) {
|
||||||
|
|
@ -111,10 +108,28 @@ impl ZwlrScreencopyFrameV1 {
|
||||||
return Err(ZwlrScreencopyFrameV1Error::InvalidBufferFormat);
|
return Err(ZwlrScreencopyFrameV1Error::InvalidBufferFormat);
|
||||||
}
|
}
|
||||||
buffer.update_framebuffer()?;
|
buffer.update_framebuffer()?;
|
||||||
if let Some(WlBufferStorage::Shm { stride, .. }) = buffer.storage.borrow_mut().deref()
|
match buffer.storage.borrow_mut().deref() {
|
||||||
&& *stride != self.rect.width() * 4
|
Some(WlBufferStorage::Shm { stride, .. }) => {
|
||||||
{
|
if *stride != self.rect.width() * 4 {
|
||||||
return Err(ZwlrScreencopyFrameV1Error::InvalidBufferStride);
|
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));
|
self.buffer.set(Some(buffer));
|
||||||
if !with_damage && let Some(global) = self.output.get() {
|
if !with_damage && let Some(global) = self.output.get() {
|
||||||
|
|
@ -134,6 +149,12 @@ impl ZwlrScreencopyFrameV1 {
|
||||||
}
|
}
|
||||||
self.pending.take();
|
self.pending.take();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cancel(&self) {
|
||||||
|
self.buffer.take();
|
||||||
|
self.pending.take();
|
||||||
|
self.send_failed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ZwlrScreencopyFrameV1RequestHandler for ZwlrScreencopyFrameV1 {
|
impl ZwlrScreencopyFrameV1RequestHandler for ZwlrScreencopyFrameV1 {
|
||||||
|
|
|
||||||
|
|
@ -104,8 +104,8 @@ impl ZwlrScreencopyManagerV1 {
|
||||||
let Some(global) = output.global.get() else {
|
let Some(global) = output.global.get() else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
let mode = global.mode.get();
|
let (width, height) = global.pixel_size();
|
||||||
let mut rect = Rect::new_sized_saturating(0, 0, mode.width, mode.height);
|
let mut rect = Rect::new_sized_saturating(0, 0, width, height);
|
||||||
if let Some(region) = region {
|
if let Some(region) = region {
|
||||||
let scale = global.persistent.scale.get().to_f64();
|
let scale = global.persistent.scale.get().to_f64();
|
||||||
let x1 = (region.x1() as f64 * scale).round() as i32;
|
let x1 = (region.x1() as f64 * scale).round() as i32;
|
||||||
|
|
|
||||||
|
|
@ -29,17 +29,6 @@ impl TestViewport {
|
||||||
Ok(())
|
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> {
|
pub fn set_destination(&self, width: i32, height: i32) -> Result<(), TestError> {
|
||||||
self.tran.send(SetDestination {
|
self.tran.send(SetDestination {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
|
|
@ -48,15 +37,6 @@ impl TestViewport {
|
||||||
})?;
|
})?;
|
||||||
Ok(())
|
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 {
|
impl Drop for TestViewport {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
it::{test_error::TestError, testrun::TestRun},
|
it::{test_error::TestError, testrun::TestRun},
|
||||||
|
rect::Rect,
|
||||||
tree::Node,
|
tree::Node,
|
||||||
},
|
},
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
|
|
@ -10,19 +11,29 @@ testcase!();
|
||||||
|
|
||||||
/// Create and map a single surface
|
/// Create and map a single surface
|
||||||
async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
|
async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
|
||||||
let ds = run.create_default_setup().await?;
|
run.backend.install_default()?;
|
||||||
|
|
||||||
let client = run.create_client().await?;
|
let client = run.create_client().await?;
|
||||||
|
|
||||||
let window = client.create_window().await?;
|
let window = client.create_window().await?;
|
||||||
window.map().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!(
|
||||||
tassert_eq!(window.tl.core.height.get(), workspace_rect.height());
|
window.tl.server.node_absolute_position(),
|
||||||
|
Rect::new_sized(
|
||||||
tassert_eq!(window.tl.server.node_absolute_position(), workspace_rect);
|
0,
|
||||||
|
2 * run.state.theme.title_plus_underline_height(),
|
||||||
|
window.tl.core.width.get(),
|
||||||
|
window.tl.core.height.get(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ testcase!();
|
||||||
|
|
||||||
/// Create and map two surfaces
|
/// Create and map two surfaces
|
||||||
async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
|
async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
|
||||||
let ds = run.create_default_setup().await?;
|
run.backend.install_default()?;
|
||||||
|
|
||||||
let client = run.create_client().await?;
|
let client = run.create_client().await?;
|
||||||
|
|
||||||
|
|
@ -21,30 +21,17 @@ async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
|
||||||
let window2 = client.create_window().await?;
|
let window2 = client.create_window().await?;
|
||||||
window2.map().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 bw = run.state.theme.sizes.border_width.get();
|
||||||
let child_width = (workspace_rect.width() - bw) / 2;
|
|
||||||
|
|
||||||
tassert_eq!(
|
tassert_eq!(
|
||||||
window.tl.server.node_absolute_position(),
|
window.tl.server.node_absolute_position(),
|
||||||
Rect::new_sized(
|
Rect::new_sized(0, otop, (800 - bw) / 2, 600 - otop).unwrap()
|
||||||
workspace_rect.x1(),
|
|
||||||
workspace_rect.y1(),
|
|
||||||
child_width,
|
|
||||||
workspace_rect.height(),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
tassert_eq!(
|
tassert_eq!(
|
||||||
window2.tl.server.node_absolute_position(),
|
window2.tl.server.node_absolute_position(),
|
||||||
Rect::new_sized(
|
Rect::new_sized((800 - bw) / 2 + bw, otop, (800 - bw) / 2, 600 - otop).unwrap()
|
||||||
workspace_rect.x1() + child_width + bw,
|
|
||||||
workspace_rect.y1(),
|
|
||||||
child_width,
|
|
||||||
workspace_rect.height(),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -48,18 +48,13 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
|
|
||||||
let mono_container = w_mono2.tl.container_parent()?;
|
let mono_container = w_mono2.tl.container_parent()?;
|
||||||
let container_pos = mono_container.tl_data().pos.get();
|
let container_pos = mono_container.tl_data().pos.get();
|
||||||
let (tab_x, tab_y) = {
|
let w_mono1_title = mono_container.render_data.borrow_mut().title_rects[0]
|
||||||
let tab_bar = mono_container.tab_bar.borrow();
|
.move_(container_pos.x1(), container_pos.y1());
|
||||||
let Some(tab_bar) = tab_bar.as_ref() else {
|
ds.mouse.abs(
|
||||||
bail!("no tab bar");
|
&ds.connector,
|
||||||
};
|
w_mono1_title.x1() as _,
|
||||||
let w_mono1_title = &tab_bar.entries[0];
|
w_mono1_title.y1() as _,
|
||||||
(
|
);
|
||||||
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;
|
client.sync().await;
|
||||||
tassert!(enters.next().is_err());
|
tassert!(enters.next().is_err());
|
||||||
|
|
|
||||||
|
|
@ -26,18 +26,12 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
|
|
||||||
let container = w_mono2.tl.container_parent()?;
|
let container = w_mono2.tl.container_parent()?;
|
||||||
let pos = container.tl_data().pos.get();
|
let pos = container.tl_data().pos.get();
|
||||||
let (tab_x, tab_y) = {
|
let w_mono1_title = container.render_data.borrow_mut().title_rects[0].move_(pos.x1(), pos.y1());
|
||||||
let tab_bar = container.tab_bar.borrow();
|
ds.mouse.abs(
|
||||||
let Some(tab_bar) = tab_bar.as_ref() else {
|
&ds.connector,
|
||||||
bail!("no tab bar");
|
w_mono1_title.x1() as f64,
|
||||||
};
|
w_mono1_title.y1() as f64,
|
||||||
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;
|
client.sync().await;
|
||||||
|
|
||||||
let enters = dss.kb.enter.expect()?;
|
let enters = dss.kb.enter.expect()?;
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -2,7 +2,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
ifs::wl_surface::xdg_surface::xdg_toplevel::STATE_SUSPENDED,
|
ifs::wl_surface::xdg_surface::xdg_toplevel::STATE_SUSPENDED,
|
||||||
it::{
|
it::{
|
||||||
test_error::{TestErrorExt, TestResult},
|
test_error::TestResult,
|
||||||
test_utils::{
|
test_utils::{
|
||||||
test_ouput_node_ext::TestOutputNodeExt, test_toplevel_node_ext::TestToplevelNodeExt,
|
test_ouput_node_ext::TestOutputNodeExt, test_toplevel_node_ext::TestToplevelNodeExt,
|
||||||
},
|
},
|
||||||
|
|
@ -10,7 +10,7 @@ use {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
isnt::std_1::collections::IsntHashSetExt,
|
isnt::std_1::collections::IsntHashSetExt,
|
||||||
std::{rc::Rc, time::Duration},
|
std::rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
testcase!();
|
testcase!();
|
||||||
|
|
@ -19,7 +19,6 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
let ds = run.create_default_setup().await?;
|
let ds = run.create_default_setup().await?;
|
||||||
|
|
||||||
let client = run.create_client().await?;
|
let client = run.create_client().await?;
|
||||||
let default_seat = client.get_default_seat().await?;
|
|
||||||
|
|
||||||
let win1 = client.create_window().await?;
|
let win1 = client.create_window().await?;
|
||||||
win1.set_color(255, 0, 0, 255);
|
win1.set_color(255, 0, 0, 255);
|
||||||
|
|
@ -45,23 +44,5 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
client.sync().await;
|
client.sync().await;
|
||||||
tassert!(win2.tl.core.states.borrow().not_contains(&STATE_SUSPENDED));
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -308,8 +308,9 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
let output_damage = connector_data.damage.borrow();
|
let output_damage = connector_data.damage.borrow();
|
||||||
tassert!(!output_damage.is_empty());
|
tassert!(!output_damage.is_empty());
|
||||||
|
|
||||||
// The test window maps its 1x1 buffer through a viewport to the full window size.
|
// Buffer damage is transformed by the damage matrix which includes the surface position
|
||||||
let expected_buffer_damage = surface_pos;
|
// 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
|
// Find the exact output damage that matches our expected buffer damage
|
||||||
let mut found_exact_buffer_damage = false;
|
let mut found_exact_buffer_damage = false;
|
||||||
|
|
@ -330,12 +331,10 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
// Test 7: Check output damage from existing window's viewport (which already has scaling)
|
// Test 7: Check output damage from existing window's viewport (which already has scaling)
|
||||||
connector_data.damage.borrow_mut().clear();
|
connector_data.damage.borrow_mut().clear();
|
||||||
|
|
||||||
// The existing window was created with create_surface_ext() which automatically creates a viewport.
|
// 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.
|
// Let's verify that the viewport's existing scaling affects buffer damage correctly
|
||||||
window.surface.viewport.set_destination(150, 100)?;
|
// First, let's modify the viewport scaling that already exists on the window
|
||||||
window.surface.commit()?;
|
window.surface.viewport.set_destination(150, 100)?; // Change scaling to 150x100
|
||||||
client.sync().await;
|
|
||||||
connector_data.damage.borrow_mut().clear();
|
|
||||||
|
|
||||||
// Add buffer damage to test viewport scaling coordinate transformation
|
// Add buffer damage to test viewport scaling coordinate transformation
|
||||||
window.surface.damage_buffer(0, 0, 1, 1)?; // Damage entire 1x1 buffer
|
window.surface.damage_buffer(0, 0, 1, 1)?; // Damage entire 1x1 buffer
|
||||||
|
|
@ -347,8 +346,8 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
let output_damage = connector_data.damage.borrow();
|
let output_damage = connector_data.damage.borrow();
|
||||||
tassert!(!output_damage.is_empty());
|
tassert!(!output_damage.is_empty());
|
||||||
|
|
||||||
// With viewporter scaling, the 1x1 buffer damage should scale to the viewport destination.
|
// With viewporter scaling, the 1x1 buffer damage should scale to 150x100
|
||||||
let surface_pos = window.surface.server.buffer_abs_pos.get();
|
// 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_scaled_damage = Rect::new_sized(0, 0, 150, 100).unwrap();
|
||||||
let expected_output_damage =
|
let expected_output_damage =
|
||||||
expected_scaled_damage.move_(surface_pos.x1(), surface_pos.y1());
|
expected_scaled_damage.move_(surface_pos.x1(), surface_pos.y1());
|
||||||
|
|
@ -403,9 +402,8 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
rotation_window.map().await?;
|
rotation_window.map().await?;
|
||||||
client.sync().await;
|
client.sync().await;
|
||||||
|
|
||||||
// Disable viewporter to rely purely on buffer dimensions.
|
// Disable viewporter by setting destination to 0x0 to rely purely on buffer dimensions
|
||||||
rotation_window.surface.viewport.unset_source()?;
|
rotation_window.surface.viewport.set_destination(0, 0)?; // Disable viewporter
|
||||||
rotation_window.surface.viewport.unset_destination()?;
|
|
||||||
|
|
||||||
// Use a rectangular buffer (4x2) so rotation has a visible geometric effect
|
// 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
|
// Attach AFTER mapping to avoid being overwritten by map()'s single-pixel buffer
|
||||||
|
|
|
||||||
|
|
@ -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
|
self.node
|
||||||
.send_port_output_buffers(&self.port, &self.buffers.borrow());
|
.send_port_output_buffers(&self.port, &self.buffers.borrow());
|
||||||
}
|
}
|
||||||
|
|
@ -633,15 +641,18 @@ impl UsrJayScreencastOwner for StartedScreencast {
|
||||||
|
|
||||||
fn ready(&self, ev: &Ready) {
|
fn ready(&self, ev: &Ready) {
|
||||||
let idx = ev.idx as usize;
|
let idx = ev.idx as usize;
|
||||||
let buffers = &*self.buffers.borrow();
|
|
||||||
let pbuffers = self.port.buffers.borrow();
|
|
||||||
let buffer = &buffers[idx];
|
|
||||||
let discard_buffer = || {
|
let discard_buffer = || {
|
||||||
self.jay_screencast.release_buffer(idx);
|
self.jay_screencast.release_buffer(idx);
|
||||||
};
|
};
|
||||||
if !self.buffers_valid.get() {
|
if !self.buffers_valid.get() {
|
||||||
return;
|
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 {
|
let Some(io) = self.port.io_buffers.get() else {
|
||||||
discard_buffer();
|
discard_buffer();
|
||||||
return;
|
return;
|
||||||
|
|
@ -767,7 +778,7 @@ pub(super) fn add_screencast_dbus_members(
|
||||||
object.add_method::<Start, _>(move |req, pr| {
|
object.add_method::<Start, _>(move |req, pr| {
|
||||||
dbus_start(&state, req, pr);
|
dbus_start(&state, req, pr);
|
||||||
});
|
});
|
||||||
object.set_property::<AvailableSourceTypes>(Variant::U32(MONITOR.0));
|
object.set_property::<AvailableSourceTypes>(Variant::U32((MONITOR | WINDOW).0));
|
||||||
object.set_property::<AvailableCursorModes>(Variant::U32(EMBEDDED.0));
|
object.set_property::<AvailableCursorModes>(Variant::U32(EMBEDDED.0));
|
||||||
object.set_property::<version>(Variant::U32(5));
|
object.set_property::<version>(Variant::U32(5));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ use {
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
on_drop_event::OnDropEvent,
|
on_drop_event::OnDropEvent,
|
||||||
rc_eq::rc_eq,
|
rc_eq::rc_eq,
|
||||||
scroller::Scroller,
|
|
||||||
threshold_counter::ThresholdCounter,
|
threshold_counter::ThresholdCounter,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -151,7 +150,6 @@ pub struct ContainerNode {
|
||||||
pub child_removed: Rc<LazyEventSource>,
|
pub child_removed: Rc<LazyEventSource>,
|
||||||
pub all_children_resized: Rc<LazyEventSource>,
|
pub all_children_resized: Rc<LazyEventSource>,
|
||||||
pub tab_bar: RefCell<Option<TabBar>>,
|
pub tab_bar: RefCell<Option<TabBar>>,
|
||||||
scroll: Scroller,
|
|
||||||
pub update_tab_textures_scheduled: Cell<bool>,
|
pub update_tab_textures_scheduled: Cell<bool>,
|
||||||
pub ephemeral: Cell<Ephemeral>,
|
pub ephemeral: Cell<Ephemeral>,
|
||||||
}
|
}
|
||||||
|
|
@ -268,7 +266,6 @@ impl ContainerNode {
|
||||||
child_removed: state.lazy_event_sources.create_source(),
|
child_removed: state.lazy_event_sources.create_source(),
|
||||||
all_children_resized: state.post_layout_event_sources.create_source(),
|
all_children_resized: state.post_layout_event_sources.create_source(),
|
||||||
tab_bar: RefCell::new(None),
|
tab_bar: RefCell::new(None),
|
||||||
scroll: Default::default(),
|
|
||||||
update_tab_textures_scheduled: Cell::new(false),
|
update_tab_textures_scheduled: Cell::new(false),
|
||||||
ephemeral: Cell::new(Ephemeral::Off),
|
ephemeral: Cell::new(Ephemeral::Off),
|
||||||
});
|
});
|
||||||
|
|
@ -796,18 +793,6 @@ impl ContainerNode {
|
||||||
self.activate_child2(child, false);
|
self.activate_child2(child, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn activate_child_from_input(
|
|
||||||
self: &Rc<Self>,
|
|
||||||
child: &NodeRef<ContainerChild>,
|
|
||||||
seat: &Rc<WlSeatGlobal>,
|
|
||||||
) {
|
|
||||||
self.activate_child(child);
|
|
||||||
child
|
|
||||||
.node
|
|
||||||
.clone()
|
|
||||||
.node_do_focus(seat, Direction::Unspecified);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn activate_child2(self: &Rc<Self>, child: &NodeRef<ContainerChild>, preserve_focus: bool) {
|
fn activate_child2(self: &Rc<Self>, child: &NodeRef<ContainerChild>, preserve_focus: bool) {
|
||||||
if let Some(mc) = self.mono_child.get() {
|
if let Some(mc) = self.mono_child.get() {
|
||||||
if mc.node.node_id() == child.node.node_id() {
|
if mc.node.node_id() == child.node.node_id() {
|
||||||
|
|
@ -1534,7 +1519,7 @@ impl ContainerNode {
|
||||||
fn button(
|
fn button(
|
||||||
self: Rc<Self>,
|
self: Rc<Self>,
|
||||||
id: CursorType,
|
id: CursorType,
|
||||||
seat: &Rc<WlSeatGlobal>,
|
_seat: &Rc<WlSeatGlobal>,
|
||||||
_time_usec: u64,
|
_time_usec: u64,
|
||||||
pressed: bool,
|
pressed: bool,
|
||||||
button: u32,
|
button: u32,
|
||||||
|
|
@ -1564,7 +1549,7 @@ impl ContainerNode {
|
||||||
if let Some(child) = children.get(&child_id) {
|
if let Some(child) = children.get(&child_id) {
|
||||||
let child_ref = child.to_ref();
|
let child_ref = child.to_ref();
|
||||||
drop(children);
|
drop(children);
|
||||||
self.activate_child_from_input(&child_ref, seat);
|
self.activate_child(&child_ref);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -2081,33 +2066,31 @@ impl Node for ContainerNode {
|
||||||
self.button(id, seat, time_usec, state == ButtonState::Pressed, button);
|
self.button(id, seat, time_usec, state == ButtonState::Pressed, button);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_on_axis_event(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, event: &PendingScroll) {
|
fn node_on_axis_event(self: Rc<Self>, _seat: &Rc<WlSeatGlobal>, event: &PendingScroll) {
|
||||||
if self.mono_child.is_none() {
|
if self.mono_child.is_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let steps = match self.scroll.handle(event) {
|
// Use vertical scroll (index 1) to switch tabs.
|
||||||
Some(steps) => steps,
|
let v = match event.v120[1].get() {
|
||||||
|
Some(v) if v != 0 => v,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let mut target = match self.mono_child.get() {
|
let mono = match self.mono_child.get() {
|
||||||
Some(m) => m,
|
Some(m) => m,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
let current_id = target.node.node_id();
|
let next = if v > 0 {
|
||||||
for _ in 0..steps.abs() {
|
// Scroll down → next tab.
|
||||||
let next = if steps > 0 {
|
mono.next().or_else(|| self.children.first())
|
||||||
target.next().or_else(|| self.children.first())
|
} else {
|
||||||
} else {
|
// Scroll up → previous tab.
|
||||||
target.prev().or_else(|| self.children.last())
|
mono.prev().or_else(|| self.children.last())
|
||||||
};
|
};
|
||||||
match next {
|
if let Some(next) = next {
|
||||||
Some(next) => target = next,
|
if next.node.node_id() != mono.node.node_id() {
|
||||||
None => break,
|
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) {
|
fn node_on_leave(&self, seat: &WlSeatGlobal) {
|
||||||
|
|
|
||||||
|
|
@ -8,25 +8,18 @@ use {
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
state::State,
|
state::State,
|
||||||
tree::{
|
tree::{
|
||||||
Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeLayerLink,
|
FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeLayerLink, NodeLocation,
|
||||||
NodeLocation, OutputNode, StackedNode, TileDragDestination, WorkspaceDragDestination,
|
OutputNode, StackedNode, TileDragDestination, WorkspaceDragDestination,
|
||||||
WorkspaceNodeId, walker::NodeVisitor,
|
WorkspaceNodeId, walker::NodeVisitor,
|
||||||
},
|
},
|
||||||
utils::{copyhashmap::CopyHashMap, linkedlist::LinkedList},
|
utils::{copyhashmap::CopyHashMap, linkedlist::LinkedList},
|
||||||
},
|
},
|
||||||
std::{
|
std::{cell::Cell, ops::Deref, rc::Rc},
|
||||||
cell::{Cell, RefCell},
|
|
||||||
mem,
|
|
||||||
ops::Deref,
|
|
||||||
rc::{Rc, Weak},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct DisplayNode {
|
pub struct DisplayNode {
|
||||||
pub id: NodeId,
|
pub id: NodeId,
|
||||||
pub extents: Cell<Rect>,
|
pub extents: Cell<Rect>,
|
||||||
visible: Cell<bool>,
|
|
||||||
suspend_restore_kb_foci: RefCell<Vec<(Rc<WlSeatGlobal>, Weak<dyn Node>)>>,
|
|
||||||
pub outputs: CopyHashMap<ConnectorId, Rc<OutputNode>>,
|
pub outputs: CopyHashMap<ConnectorId, Rc<OutputNode>>,
|
||||||
pub stacked: Rc<LinkedList<Rc<dyn StackedNode>>>,
|
pub stacked: Rc<LinkedList<Rc<dyn StackedNode>>>,
|
||||||
pub stacked_above_layers: Rc<LinkedList<Rc<dyn StackedNode>>>,
|
pub stacked_above_layers: Rc<LinkedList<Rc<dyn StackedNode>>>,
|
||||||
|
|
@ -38,8 +31,6 @@ impl DisplayNode {
|
||||||
let slf = Self {
|
let slf = Self {
|
||||||
id,
|
id,
|
||||||
extents: Default::default(),
|
extents: Default::default(),
|
||||||
visible: Default::default(),
|
|
||||||
suspend_restore_kb_foci: Default::default(),
|
|
||||||
outputs: Default::default(),
|
outputs: Default::default(),
|
||||||
stacked: Default::default(),
|
stacked: Default::default(),
|
||||||
stacked_above_layers: Default::default(),
|
stacked_above_layers: Default::default(),
|
||||||
|
|
@ -80,17 +71,6 @@ impl DisplayNode {
|
||||||
|
|
||||||
pub fn update_visible(&self, state: &State) {
|
pub fn update_visible(&self, state: &State) {
|
||||||
let visible = state.root_visible();
|
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() {
|
for output in self.outputs.lock().values() {
|
||||||
output.update_visible();
|
output.update_visible();
|
||||||
}
|
}
|
||||||
|
|
@ -102,20 +82,6 @@ impl DisplayNode {
|
||||||
for seat in state.globals.seats.lock().values() {
|
for seat in state.globals.seats.lock().values() {
|
||||||
seat.set_visible(visible);
|
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 {
|
if visible {
|
||||||
state.damage(self.extents.get());
|
state.damage(self.extents.get());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -460,9 +460,15 @@ impl OutputNode {
|
||||||
}
|
}
|
||||||
self.lock_surface.take();
|
self.lock_surface.take();
|
||||||
self.jay_outputs.clear();
|
self.jay_outputs.clear();
|
||||||
self.screencasts.clear();
|
for screencast in self.screencasts.lock().drain_values() {
|
||||||
self.screencopies.clear();
|
screencast.do_destroy();
|
||||||
self.ext_copy_sessions.clear();
|
}
|
||||||
|
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.ext_workspace_groups.clear();
|
||||||
self.latch_event.clear();
|
self.latch_event.clear();
|
||||||
self.vblank_event.clear();
|
self.vblank_event.clear();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue