From e60738401d059002ff366b489e630ef9046698c0 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 17 Dec 2024 15:13:24 -0800 Subject: [PATCH] WIP Subsurfaces for drag surfaces Use `Icon::Surface` instead of `Icon::Buffer`, so we can then create subsurfaces of the `wl_surface`. Using `.screenshot()` then copying to an shm buffer is suboptimal, but this does seem overall better than with the older Iced version when a drag surface didn't appear until a Vulkan surface could be created for it. --- Cargo.toml | 1 + winit/Cargo.toml | 2 + winit/src/platform_specific/mod.rs | 18 +++++-- winit/src/platform_specific/wayland/mod.rs | 36 ++++++++++++++ .../wayland/subsurface_widget.rs | 47 ++++++++++++++++++- winit/src/program.rs | 29 ++++++++---- 6 files changed, 118 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ca5c62c343..6e08769a0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -214,6 +214,7 @@ wasm-timer = "0.2" web-time = "1.1" wgpu = "22.0" 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 f16d109e86..b66d4ab39a 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -62,12 +62,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/platform_specific/mod.rs b/winit/src/platform_specific/mod.rs index 530f7e00fe..070452f89c 100644 --- a/winit/src/platform_specific/mod.rs +++ b/winit/src/platform_specific/mod.rs @@ -5,6 +5,7 @@ use std::collections::HashMap; use iced_graphics::Compositor; use iced_runtime::{core::window, user_interface, Debug}; +use raw_window_handle::HasWindowHandle; #[cfg(all(feature = "wayland", target_os = "linux"))] pub mod wayland; @@ -69,7 +70,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"))] { @@ -78,12 +79,13 @@ impl PlatformSpecific { }; use wayland_backend::client::ObjectId; - let Ok(backend) = window.rwh_06_display_handle().display_handle() + let Some(backend) = self.wayland.display_handle() else { log::error!("No display handle"); return; }; + // TODO use existing conn let conn = match backend.as_raw() { raw_window_handle::RawDisplayHandle::Wayland( wayland_display_handle, @@ -102,7 +104,7 @@ impl PlatformSpecific { } }; - 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; }; @@ -137,6 +139,14 @@ impl PlatformSpecific { self.wayland.update_subsurfaces(id, &wl_surface); } } + + pub(crate) fn create_surface(&mut self, width: u32, height: u32, data: &[u8]) -> Option> { + #[cfg(all(feature = "wayland", target_os = "linux"))] + { + return self.wayland.create_surface(width, height, data); + } + None + } } pub type UserInterfaces<'a, P> = HashMap< @@ -185,4 +195,4 @@ pub(crate) fn handle_event<'a, P, C>( ); } } -} +} \ No newline at end of file diff --git a/winit/src/platform_specific/wayland/mod.rs b/winit/src/platform_specific/wayland/mod.rs index 315bda5ed0..df99066204 100644 --- a/winit/src/platform_specific/wayland/mod.rs +++ b/winit/src/platform_specific/wayland/mod.rs @@ -112,6 +112,10 @@ impl PlatformSpecific { } impl WaylandSpecific { + pub(crate) fn display_handle(&self) -> Option { + self.display_handle.as_ref()?.display_handle().ok() + } + pub(crate) fn handle_event<'a, P, C>( &mut self, e: SctkEvent, @@ -199,5 +203,37 @@ impl WaylandSpecific { surface_subsurfaces, &subsurfaces, ); + + wl_surface.commit(); // XXX for drag surface + } + + // WIP cfg raw-window-handle + pub(crate) fn create_surface(&mut self, width: u32, height: u32, data: &[u8]) -> Option> { + if let Some(subsurface_state) = self.subsurface_state.as_mut() { + let wl_surface = subsurface_state.create_surface(width, height, data); + Some(Box::new(Window(wl_surface))) + } else { + None + } + } +} +struct Window(WlSurface); +impl HasWindowHandle for Window { + fn window_handle(&self) -> Result, 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) { + // TODO destroy when drag ends + //self.0.destroy(); } } +use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle}; +use wayland_client::Proxy; diff --git a/winit/src/platform_specific/wayland/subsurface_widget.rs b/winit/src/platform_specific/wayland/subsurface_widget.rs index fb3ff9ac09..4e174d0a4e 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::{ delegate_noop, protocol::{ @@ -251,6 +253,7 @@ impl PartialEq for SubsurfaceBuffer { } } + impl Dispatch for SctkState { fn event( _: &mut SctkState, @@ -288,6 +291,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, @@ -325,6 +345,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)] @@ -342,6 +371,21 @@ pub struct SubsurfaceState { } impl SubsurfaceState { + pub fn create_surface(&self, width: u32, height: u32, data: &[u8]) -> WlSurface { + 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); + + let surface = self + .wl_compositor + .create_surface(&self.qh, SurfaceData::new(None, 1)); + surface.damage_buffer(0, 0, width as i32, height as i32); + buffer.attach_to(&surface); + surface.commit(); + surface + } + fn create_subsurface(&self, parent: &WlSurface) -> SubsurfaceInstance { let wl_surface = self .wl_compositor @@ -572,6 +616,7 @@ impl Drop for SubsurfaceInstance { } } +#[derive(Debug)] pub(crate) struct SubsurfaceInfo { pub buffer: SubsurfaceBuffer, pub bounds: Rectangle, diff --git a/winit/src/program.rs b/winit/src/program.rs index c673106af1..c93973decb 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -964,9 +964,7 @@ async fn run_instance<'a, P, C>( scale_factor: state.scale_factor(), }, Default::default(), - ); - platform_specific_handler - .clear_subsurface_list(); + );; let mut bytes = compositor.screenshot( &mut renderer, &viewport, @@ -977,17 +975,28 @@ async fn run_instance<'a, P, C>( // rgba -> argb little endian pix.swap(0, 2); } - Icon::Buffer { - data: Arc::new(bytes), - width: viewport.physical_width(), - height: viewport.physical_height(), - transparent: true, + // update subsurfaces + if let Some(surface) = platform_specific_handler.create_surface(viewport.physical_width(), viewport.physical_height(), &bytes) { + let id = window::Id::unique(); // XXX remove at end of drag + platform_specific_handler + .update_subsurfaces(id, &surface); + Icon::Surface(dnd::DndSurface(Arc::new(surface))) + } else { + platform_specific_handler + .clear_subsurface_list(); + Icon::Buffer { + data: Arc::new(bytes), + width: viewport.physical_width(), + height: viewport.physical_height(), + transparent: true, + } } }, ); clipboard.start_dnd_winit( internal, + // false DndSurface(Arc::new(Box::new(window.raw.clone()))), icon_surface, content, @@ -1191,7 +1200,7 @@ async fn run_instance<'a, P, C>( cursor, ); platform_specific_handler - .update_subsurfaces(id, window.raw.as_ref()); + .update_subsurfaces(id, window.raw.rwh_06_window_handle()); debug.draw_finished(); if new_mouse_interaction != window.mouse_interaction { @@ -1276,7 +1285,7 @@ async fn run_instance<'a, P, C>( window.state.cursor(), ); platform_specific_handler - .update_subsurfaces(id, window.raw.as_ref()); + .update_subsurfaces(id, window.raw.rwh_06_window_handle()); debug.draw_finished(); if new_mouse_interaction != window.mouse_interaction