examples: Add sctk_subsurface_img

Test scrolling. Single color buffers in `sctk_subsurface` aren't good
for comparing it, and `sctk_subsurface_gst` isn't working on latest gst
with explicit sync (and AppSync isn't accepting the dmabuf when I try to
modify it.)
This commit is contained in:
Ian Douglas Scott 2025-05-08 18:51:53 -07:00 committed by Ashley Wulber
parent 5865264964
commit d0b4d0e9c9
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
2 changed files with 156 additions and 0 deletions

View file

@ -0,0 +1,21 @@
[package]
name = "sctk_subsurface_img"
version = "0.1.0"
edition = "2021"
[dependencies]
iced = { path = "../..", default-features = false, features = [
"tokio",
"wayland",
"winit",
"debug",
"tiny-skia",
"image",
] }
iced_runtime = { path = "../../runtime" }
env_logger = "0.10"
futures-channel = "0.3.29"
calloop = "0.13"
rustix = { version = "0.38.30", features = ["fs", "shm"] }
cctk.workspace = true
image = { workspace = true, features = ["png"] }

View file

@ -0,0 +1,135 @@
use cctk::sctk::reexports::client::protocol::wl_shm;
use iced::{
platform_specific::shell::subsurface_widget::{
self, Shmbuf, SubsurfaceBuffer,
},
window::{self, Id, Settings},
Element, Length, Subscription, Task,
};
use image::{ImageReader, Pixel};
use rustix::{io::Errno, shm::ShmOFlags};
use std::{
env,
os::fd::OwnedFd,
path::Path,
sync::Arc,
time::{SystemTime, UNIX_EPOCH},
};
fn main() -> iced::Result {
let args = env::args();
if args.len() != 2 {
eprintln!("usage: sctk_subsurface_img [image path]");
return Ok(());
}
let path = args.skip(1).next().unwrap();
if !Path::new(&path).exists() {
eprintln!("File `{path}` not found.");
return Ok(());
}
iced::daemon(
SubsurfaceApp::title,
SubsurfaceApp::update,
SubsurfaceApp::view,
)
.subscription(SubsurfaceApp::subscription)
.run_with(|| SubsurfaceApp::new(path))
}
#[derive(Debug, Clone)]
struct SubsurfaceApp {
path: String,
buffer: SubsurfaceBuffer,
}
#[derive(Debug, Clone)]
pub enum Message {
Id(Id),
}
impl SubsurfaceApp {
fn new(path: String) -> (SubsurfaceApp, Task<Message>) {
let img = ImageReader::open(&path)
.unwrap()
.decode()
.unwrap()
.to_rgba8();
let fd = create_memfile().unwrap();
for pixel in img.pixels() {
let [r, g, b, a] = <[u8; 4]>::try_from(pixel.channels()).unwrap();
rustix::io::write(&fd, &[b, g, r, a]).unwrap();
}
let shmbuf = Shmbuf {
fd,
offset: 0,
width: img.width() as i32,
height: img.height() as i32,
stride: img.width() as i32 * 4,
format: wl_shm::Format::Xrgb8888,
};
let buffer = SubsurfaceBuffer::new(Arc::new(shmbuf.into())).0;
(
SubsurfaceApp { path, buffer },
iced::window::open(Settings {
..Default::default()
})
.1
.map(Message::Id),
)
}
fn title(&self, _id: window::Id) -> String {
String::from("SubsurfaceApp")
}
fn update(&mut self, message: Message) -> Task<Message> {
match message {
Message::Id(_) => {}
}
Task::none()
}
fn view(&self, _id: window::Id) -> Element<Message> {
// TODO compare side-by-side with image widget; key bind to toggle?
let image = subsurface_widget::Subsurface::new(self.buffer.clone())
.content_fit(iced::ContentFit::None);
/*
let image = iced::widget::image::Image::new(&self.path)
.content_fit(iced::ContentFit::None);
*/
iced::widget::scrollable(image).into()
}
fn subscription(&self) -> Subscription<Message> {
Subscription::none()
}
}
fn create_memfile() -> rustix::io::Result<OwnedFd> {
loop {
let flags = ShmOFlags::CREATE | ShmOFlags::EXCL | ShmOFlags::RDWR;
let time = SystemTime::now();
let name = format!(
"/iced-sctk-{}",
time.duration_since(UNIX_EPOCH).unwrap().subsec_nanos()
);
match rustix::io::retry_on_intr(|| {
rustix::shm::shm_open(&name, flags, 0600.into())
}) {
Ok(fd) => match rustix::shm::shm_unlink(&name) {
Ok(_) => return Ok(fd),
Err(errno) => {
return Err(errno.into());
}
},
Err(Errno::EXIST) => {
continue;
}
Err(err) => return Err(err.into()),
}
}
}