Skip to content

Commit

Permalink
WIP subsurface
Browse files Browse the repository at this point in the history
subsurface: don't cause panic with dnd

drag surface subsurface

Call `wl_surface::damage`

subsurface: Refcounting new `wl_buffers` to handle release
  • Loading branch information
ids1024 committed Feb 6, 2024
1 parent 575de00 commit a5ad5c8
Show file tree
Hide file tree
Showing 14 changed files with 1,074 additions and 0 deletions.
14 changes: 14 additions & 0 deletions examples/sctk_subsurface/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "sctk_subsurface"
version = "0.1.0"
edition = "2021"

[dependencies]
sctk = { package = "smithay-client-toolkit", git = "https://github.com/smithay/client-toolkit", rev = "828b1eb" }
iced = { path = "../..", default-features = false, features = ["wayland", "debug", "a11y"] }
iced_runtime = { path = "../../runtime" }
iced_sctk = { path = "../../sctk" }
env_logger = "0.10"
futures-channel = "0.3.29"
calloop = "0.12.3"
rustix = { version = "0.38.30", features = ["fs", "shm"] }
100 changes: 100 additions & 0 deletions examples/sctk_subsurface/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Shows a subsurface with a 1x1 px red buffer, stretch to window size

use iced::{
event::wayland::Event as WaylandEvent, wayland::InitialSurface,
widget::text, window, Application, Command, Element, Length, Subscription,
Theme,
};
use iced_sctk::subsurface_widget::SubsurfaceBuffer;
use sctk::reexports::client::{Connection, Proxy};

mod wayland;

fn main() {
let mut settings = iced::Settings::default();
settings.initial_surface = InitialSurface::XdgWindow(Default::default());
SubsurfaceApp::run(settings).unwrap();
}

#[derive(Debug, Clone, Default)]
struct SubsurfaceApp {
connection: Option<Connection>,
red_buffer: Option<SubsurfaceBuffer>,
}

#[derive(Debug, Clone)]
pub enum Message {
WaylandEvent(WaylandEvent),
Wayland(wayland::Event),
}

impl Application for SubsurfaceApp {
type Executor = iced::executor::Default;
type Message = Message;
type Flags = ();
type Theme = Theme;

fn new(_flags: ()) -> (SubsurfaceApp, Command<Self::Message>) {
(
SubsurfaceApp {
..SubsurfaceApp::default()
},
Command::none(),
)
}

fn title(&self, _id: window::Id) -> String {
String::from("SubsurfaceApp")
}

fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
match message {
Message::WaylandEvent(evt) => match evt {
WaylandEvent::Output(_evt, output) => {
if self.connection.is_none() {
if let Some(backend) = output.backend().upgrade() {
self.connection =
Some(Connection::from_backend(backend));
}
}
}
_ => {}
},
Message::Wayland(evt) => match evt {
wayland::Event::RedBuffer(buffer) => {
self.red_buffer = Some(buffer);
}
},
}
Command::none()
}

fn view(&self, _id: window::Id) -> Element<Self::Message> {
if let Some(buffer) = &self.red_buffer {
iced_sctk::subsurface_widget::Subsurface::new(1, 1, buffer)
.width(Length::Fill)
.height(Length::Fill)
.into()
} else {
text("No subsurface").into()
}
}

fn subscription(&self) -> Subscription<Self::Message> {
let mut subscriptions = vec![iced::event::listen_with(|evt, _| {
if let iced::Event::PlatformSpecific(
iced::event::PlatformSpecific::Wayland(evt),
) = evt
{
Some(Message::WaylandEvent(evt))
} else {
None
}
})];
if let Some(connection) = &self.connection {
subscriptions
.push(wayland::subscription(connection).map(Message::Wayland));
}
Subscription::batch(subscriptions)
}
}
125 changes: 125 additions & 0 deletions examples/sctk_subsurface/src/wayland.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
use futures_channel::mpsc;
use iced::futures::{FutureExt, SinkExt};
use iced_sctk::subsurface_widget::{Shmbuf, SubsurfaceBuffer};
use rustix::{io::Errno, shm::ShmOFlags};
use sctk::{
reexports::{
calloop_wayland_source::WaylandSource,
client::{
delegate_noop,
globals::registry_queue_init,
protocol::{wl_buffer::WlBuffer, wl_shm},
Connection,
},
},
registry::{ProvidesRegistryState, RegistryState},
shm::{Shm, ShmHandler},
};
use std::{
os::fd::OwnedFd,
sync::Arc,
thread,
time::{SystemTime, UNIX_EPOCH},
};

#[derive(Debug, Clone)]
pub enum Event {
RedBuffer(SubsurfaceBuffer),
}

struct AppData {
registry_state: RegistryState,
shm_state: Shm,
}

impl ProvidesRegistryState for AppData {
fn registry(&mut self) -> &mut RegistryState {
&mut self.registry_state
}

sctk::registry_handlers!();
}

impl ShmHandler for AppData {
fn shm_state(&mut self) -> &mut Shm {
&mut self.shm_state
}
}

pub fn subscription(connection: &Connection) -> iced::Subscription<Event> {
let connection = connection.clone();
iced::subscription::run_with_id(
"wayland-sub",
async { start(connection).await }.flatten_stream(),
)
}

async fn start(conn: Connection) -> mpsc::Receiver<Event> {
let (mut sender, receiver) = mpsc::channel(20);

let (globals, event_queue) = registry_queue_init(&conn).unwrap();
let qh = event_queue.handle();

let mut app_data = AppData {
registry_state: RegistryState::new(&globals),
shm_state: Shm::bind(&globals, &qh).unwrap(),
};

let fd = create_memfile().unwrap();
rustix::io::write(&fd, &[0, 0, 255, 255]).unwrap();

let shmbuf = Shmbuf {
fd,
offset: 0,
width: 1,
height: 1,
stride: 4,
format: wl_shm::Format::Xrgb8888,
};

let buffer = SubsurfaceBuffer::new(Arc::new(shmbuf.into())).0;
let _ = sender.send(Event::RedBuffer(buffer)).await;

thread::spawn(move || {
let mut event_loop = calloop::EventLoop::try_new().unwrap();
WaylandSource::new(conn, event_queue)
.insert(event_loop.handle())
.unwrap();
loop {
event_loop.dispatch(None, &mut app_data).unwrap();
}
});

receiver
}

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()),
}
}
}

delegate_noop!(AppData: ignore WlBuffer);
sctk::delegate_registry!(AppData);
sctk::delegate_shm!(AppData);
18 changes: 18 additions & 0 deletions examples/sctk_subsurface_gst/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "sctk_subsurface_gst"
version = "0.1.0"
edition = "2021"

[dependencies]
sctk = { package = "smithay-client-toolkit", git = "https://github.com/smithay/client-toolkit", rev = "828b1eb" }
iced = { path = "../..", default-features = false, features = ["wayland", "debug", "a11y"] }
iced_runtime = { path = "../../runtime" }
iced_sctk = { path = "../../sctk" }
env_logger = "0.10"
futures-channel = "0.3.29"
calloop = "0.12.3"
gst = { package = "gstreamer", version = "0.21.3" }
gst-app = { package = "gstreamer-app", version = "0.21.2" }
gst-video = { package = "gstreamer-video", version = "0.21.2" }
gst-allocators = { package = "gstreamer-allocators", version = "0.21.2" }
drm-fourcc = "2.2.0"
84 changes: 84 additions & 0 deletions examples/sctk_subsurface_gst/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Shows a subsurface with a 1x1 px red buffer, stretch to window size

use iced::{
wayland::InitialSurface, widget::text, window, Application, Command,
Element, Length, Subscription, Theme,
};
use iced_sctk::subsurface_widget::SubsurfaceBuffer;
use std::{env, path::Path};

mod pipewire;

fn main() {
let args = env::args();
if args.len() != 2 {
eprintln!("usage: sctk_subsurface_gst [h264 mp4 path]");
return;
}
let path = args.skip(1).next().unwrap();
if !Path::new(&path).exists() {
eprintln!("File `{path}` not found.");
return;
}
let mut settings = iced::Settings::with_flags(path);
settings.initial_surface = InitialSurface::XdgWindow(Default::default());
SubsurfaceApp::run(settings).unwrap();
}

#[derive(Debug, Clone, Default)]
struct SubsurfaceApp {
path: String,
buffer: Option<SubsurfaceBuffer>,
}

#[derive(Debug, Clone)]
pub enum Message {
Pipewire(pipewire::Event),
}

impl Application for SubsurfaceApp {
type Executor = iced::executor::Default;
type Message = Message;
type Flags = String;
type Theme = Theme;

fn new(flags: String) -> (SubsurfaceApp, Command<Self::Message>) {
(
SubsurfaceApp {
path: flags,
..SubsurfaceApp::default()
},
Command::none(),
)
}

fn title(&self, _id: window::Id) -> String {
String::from("SubsurfaceApp")
}

fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
match message {
Message::Pipewire(evt) => match evt {
pipewire::Event::Frame(subsurface_buffer) => {
self.buffer = Some(subsurface_buffer);
}
},
}
Command::none()
}

fn view(&self, _id: window::Id) -> Element<Self::Message> {
if let Some(buffer) = &self.buffer {
iced_sctk::subsurface_widget::Subsurface::new(1, 1, buffer)
.width(Length::Fill)
.height(Length::Fill)
.into()
} else {
text("No subsurface").into()
}
}

fn subscription(&self) -> Subscription<Self::Message> {
pipewire::subscription(&self.path).map(Message::Pipewire)
}
}
Loading

0 comments on commit a5ad5c8

Please sign in to comment.