diff --git a/Cargo.toml b/Cargo.toml
index 21c5c640..099d57bd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -296,6 +296,7 @@ wgpu = { version = "27.0", default-features = false, features = [
"wgsl",
] }
wayland-protocols = { version = "0.32.1", features = ["staging"] }
+wayland-client = { version = "0.31.5" }
# web-time = "1.1"
diff --git a/winit/Cargo.toml b/winit/Cargo.toml
index d1850590..e72b2fca 100644
--- a/winit/Cargo.toml
+++ b/winit/Cargo.toml
@@ -73,12 +73,14 @@ cctk.workspace = true
cctk.optional = true
wayland-protocols.workspace = true
wayland-protocols.optional = true
+wayland-client.workspace = true
wayland-backend = { version = "0.3.1", features = [
"client_system",
], optional = true }
xkbcommon = { version = "0.7", features = ["wayland"], optional = true }
xkbcommon-dl = { version = "0.4.1", optional = true }
xkeysym = { version = "0.2.0", optional = true }
+rustix = { version = "0.38" }
[target.'cfg(target_os = "windows")'.dependencies]
winapi.workspace = true
diff --git a/winit/src/lib.rs b/winit/src/lib.rs
index a2da3372..6060b87d 100644
--- a/winit/src/lib.rs
+++ b/winit/src/lib.rs
@@ -24,6 +24,7 @@ pub use iced_program as program;
pub use program::core;
pub use program::graphics;
pub use program::runtime;
+use raw_window_handle::HasWindowHandle;
pub use runtime::futures;
use window_clipboard::mime::ClipboardStoreData;
pub use winit;
@@ -657,6 +658,10 @@ async fn run_instance
(
let mut clipboard = Clipboard::unconnected();
let mut cur_dnd_surface: Option = None;
+ let mut dnd_surface: Option<
+ Arc>,
+ > = None;
+
#[cfg(feature = "a11y")]
let (mut adapters, mut a11y_enabled) = (Default::default(), false);
// let (mut adapters, mut a11y_enabled) = if let Some((main_id, title, raw)) =
@@ -1133,7 +1138,8 @@ async fn run_instance(
},
cursor,
);
- platform_specific_handler.clear_subsurface_list();
+ platform_specific_handler
+ .update_subsurfaces(id, window.raw.rwh_06_window_handle());
draw_span.finish();
if let user_interface::State::Updated {
@@ -1508,7 +1514,14 @@ async fn run_instance
(
dnd::DndEvent::Offer(..) => {
events.push((cur_dnd_surface, core::Event::Dnd(e)));
}
- dnd::DndEvent::Source(_) => {
+ dnd::DndEvent::Source(evt) => {
+ match evt {
+ dnd::SourceEvent::Finished
+ | dnd::SourceEvent::Cancelled => {
+ dnd_surface = None;
+ }
+ _ => {}
+ }
for w in window_manager.ids() {
events.push((Some(w), core::Event::Dnd(e.clone())));
}
diff --git a/winit/src/platform_specific/mod.rs b/winit/src/platform_specific/mod.rs
index ef65b2c5..cd8053c5 100644
--- a/winit/src/platform_specific/mod.rs
+++ b/winit/src/platform_specific/mod.rs
@@ -7,6 +7,7 @@ use std::collections::HashMap;
use cctk::sctk::reexports::client::Connection;
use iced_graphics::{Compositor, compositor};
use iced_runtime::{core::window, platform_specific, user_interface};
+use raw_window_handle::HasWindowHandle;
#[cfg(all(feature = "wayland", target_os = "linux"))]
pub mod wayland;
@@ -82,7 +83,7 @@ impl PlatformSpecific {
pub(crate) fn update_subsurfaces(
&mut self,
id: window::Id,
- window: &dyn winit::window::Window,
+ window: &dyn HasWindowHandle,
) {
#[cfg(all(feature = "wayland", target_os = "linux"))]
{
@@ -91,31 +92,12 @@ impl PlatformSpecific {
};
use wayland_backend::client::ObjectId;
- let Ok(backend) = window.rwh_06_display_handle().display_handle()
- else {
- log::error!("No display handle");
+ let Some(conn) = self.wayland.conn() else {
+ log::error!("No Wayland conn");
return;
};
- let conn = match backend.as_raw() {
- raw_window_handle::RawDisplayHandle::Wayland(
- wayland_display_handle,
- ) => {
- let backend = unsafe {
- Backend::from_foreign_display(
- wayland_display_handle.display.as_ptr().cast(),
- )
- };
- cctk::sctk::reexports::client::Connection::from_backend(
- backend,
- )
- }
- _ => {
- return;
- }
- };
-
- let Ok(raw) = window.rwh_06_window_handle().window_handle() else {
+ let Ok(raw) = window.window_handle() else {
log::error!("Invalid window handle {id:?}");
return;
};
@@ -150,6 +132,31 @@ impl PlatformSpecific {
self.wayland.update_subsurfaces(id, &wl_surface);
}
}
+
+ pub(crate) fn create_surface(
+ &mut self,
+ ) -> Option> {
+ #[cfg(all(feature = "wayland", target_os = "linux"))]
+ {
+ return self.wayland.create_surface();
+ }
+ None
+ }
+
+ pub(crate) fn update_surface_shm(
+ &mut self,
+ surface: &dyn HasWindowHandle,
+ width: u32,
+ height: u32,
+ data: &[u8],
+ ) {
+ #[cfg(all(feature = "wayland", target_os = "linux"))]
+ {
+ return self
+ .wayland
+ .update_surface_shm(surface, width, height, data);
+ }
+ }
}
pub(crate) fn handle_event<'a, P>(
diff --git a/winit/src/platform_specific/wayland/mod.rs b/winit/src/platform_specific/wayland/mod.rs
index 3fca711a..bf125e12 100644
--- a/winit/src/platform_specific/wayland/mod.rs
+++ b/winit/src/platform_specific/wayland/mod.rs
@@ -18,10 +18,13 @@ use cursor_icon::CursorIcon;
use iced_futures::futures::channel::mpsc;
use iced_graphics::{Compositor, compositor};
use iced_runtime::core::window;
+use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle};
+use raw_window_handle::{HasRawDisplayHandle, RawWindowHandle};
use sctk_event::SctkEvent;
use std::{collections::HashMap, sync::Arc};
use subsurface_widget::{SubsurfaceInstance, SubsurfaceState};
use wayland_backend::client::ObjectId;
+use wayland_client::{Connection, Proxy};
use winit::event_loop::OwnedDisplayHandle;
pub(crate) enum Action {
@@ -60,6 +63,7 @@ pub(crate) struct WaylandSpecific {
proxy: Option,
sender: Option>,
display_handle: Option,
+ conn: Option,
modifiers: Modifiers,
surface_ids: HashMap,
subsurface_state: Option,
@@ -74,6 +78,26 @@ impl PlatformSpecific {
display: OwnedDisplayHandle,
) -> Self {
self.wayland.winit_event_sender = Some(tx);
+ self.wayland.conn = match display.raw_display_handle() {
+ Ok(raw_window_handle::RawDisplayHandle::Wayland(
+ wayland_display_handle,
+ )) => {
+ let backend = unsafe {
+ wayland_backend::client::Backend::from_foreign_display(
+ wayland_display_handle.display.as_ptr().cast(),
+ )
+ };
+ Some(Connection::from_backend(backend))
+ }
+ Ok(_) => {
+ log::error!("Non-Wayland display handle");
+ None
+ }
+ Err(_) => {
+ log::error!("No display handle");
+ None
+ }
+ };
self.wayland.display_handle = Some(display);
self.wayland.proxy = Some(raw);
// TODO remove this
@@ -111,6 +135,10 @@ impl PlatformSpecific {
}
impl WaylandSpecific {
+ pub(crate) fn conn(&self) -> Option<&Connection> {
+ self.conn.as_ref()
+ }
+
pub(crate) fn handle_event<'a, P>(
&mut self,
e: SctkEvent,
@@ -135,6 +163,7 @@ impl WaylandSpecific {
proxy,
sender,
display_handle,
+ conn,
surface_ids,
modifiers,
subsurface_state,
@@ -198,4 +227,70 @@ impl WaylandSpecific {
&subsurfaces,
);
}
+
+ pub(crate) fn create_surface(
+ &mut self,
+ ) -> Option> {
+ if let Some(subsurface_state) = self.subsurface_state.as_mut() {
+ let wl_surface = subsurface_state.create_surface();
+ Some(Box::new(Window(wl_surface)))
+ } else {
+ None
+ }
+ }
+
+ pub(crate) fn update_surface_shm(
+ &mut self,
+ window: &dyn HasWindowHandle,
+ width: u32,
+ height: u32,
+ data: &[u8],
+ ) {
+ if let Some(subsurface_state) = self.subsurface_state.as_mut() {
+ if let RawWindowHandle::Wayland(window) =
+ window.window_handle().unwrap().as_raw()
+ {
+ let id = unsafe {
+ ObjectId::from_ptr(
+ WlSurface::interface(),
+ window.surface.as_ptr().cast(),
+ )
+ .unwrap()
+ };
+ let surface =
+ WlSurface::from_id(self.conn.as_ref().unwrap(), id)
+ .unwrap();
+ subsurface_state
+ .update_surface_shm(&surface, width, height, data);
+ }
+ }
+ }
+}
+
+struct Window(WlSurface);
+
+impl HasWindowHandle for Window {
+ fn window_handle(
+ &self,
+ ) -> Result<
+ raw_window_handle::WindowHandle<'_>,
+ raw_window_handle::HandleError,
+ > {
+ Ok(unsafe {
+ raw_window_handle::WindowHandle::borrow_raw(
+ raw_window_handle::RawWindowHandle::Wayland(
+ raw_window_handle::WaylandWindowHandle::new(
+ std::ptr::NonNull::new(self.0.id().as_ptr() as *mut _)
+ .unwrap(),
+ ),
+ ),
+ )
+ })
+ }
+}
+
+impl Drop for Window {
+ fn drop(&mut self) {
+ self.0.destroy();
+ }
}
diff --git a/winit/src/platform_specific/wayland/subsurface_widget.rs b/winit/src/platform_specific/wayland/subsurface_widget.rs
index 4d9e1cba..b2e716c9 100644
--- a/winit/src/platform_specific/wayland/subsurface_widget.rs
+++ b/winit/src/platform_specific/wayland/subsurface_widget.rs
@@ -23,7 +23,9 @@ use std::{
use crate::futures::futures::channel::oneshot;
use cctk::sctk::{
compositor::SurfaceData,
- globals::GlobalData,
+ error::GlobalError,
+ globals::{GlobalData, ProvidesBoundGlobal},
+ shm::slot::SlotPool,
reexports::client::{
Connection, Dispatch, Proxy, QueueHandle, delegate_noop,
protocol::{
@@ -250,6 +252,7 @@ impl PartialEq for SubsurfaceBuffer {
}
}
+
impl Dispatch for SctkState {
fn event(
_: &mut SctkState,
@@ -287,6 +290,23 @@ impl Dispatch for SctkState {
}
}
+impl Dispatch for SctkState {
+ fn event(
+ _: &mut SctkState,
+ _: &WlBuffer,
+ event: wl_buffer::Event,
+ _: &GlobalData,
+ _: &Connection,
+ _: &QueueHandle,
+ ) {
+ match event {
+ wl_buffer::Event::Release => {
+ }
+ _ => unreachable!(),
+ }
+ }
+}
+
impl Dispatch for SctkState {
fn event(
_: &mut SctkState,
@@ -324,6 +344,15 @@ impl Hash for WeakBufferSource {
}
}
+// Implement `ProvidesBoundGlobal` to use `SlotPool`
+struct ShmGlobal<'a>(&'a WlShm);
+
+impl<'a> ProvidesBoundGlobal for ShmGlobal<'a> {
+ fn bound_global(&self) -> Result {
+ Ok(self.0.clone())
+ }
+}
+
// create wl_buffer from BufferSource (avoid create_immed?)
// release
#[derive(Debug, Clone)]
@@ -341,6 +370,22 @@ pub struct SubsurfaceState {
}
impl SubsurfaceState {
+ pub fn create_surface(&self) -> WlSurface {
+ self
+ .wl_compositor
+ .create_surface(&self.qh, SurfaceData::new(None, 1))
+ }
+
+ pub fn update_surface_shm(&self, surface: &WlSurface, width: u32, height: u32, data: &[u8]) {
+ let shm = ShmGlobal(&self.wl_shm);
+ let mut pool = SlotPool::new(width as usize * height as usize * 4, &shm).unwrap();
+ let (buffer, canvas) = pool.create_buffer(width as i32, height as i32, width as i32 * 4, wl_shm::Format::Argb8888).unwrap();
+ canvas[0..width as usize * height as usize * 4].copy_from_slice(data);
+ surface.damage_buffer(0, 0, width as i32, height as i32);
+ buffer.attach_to(&surface);
+ surface.commit();
+ }
+
fn create_subsurface(&self, parent: &WlSurface) -> SubsurfaceInstance {
let wl_surface = self
.wl_compositor
@@ -571,6 +616,7 @@ impl Drop for SubsurfaceInstance {
}
}
+#[derive(Debug)]
pub(crate) struct SubsurfaceInfo {
pub buffer: SubsurfaceBuffer,
pub bounds: Rectangle,