From eb2cd55e16ad69451aa94b21c88ac9c3bb26d282 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 2 Jan 2025 11:10:24 -0800 Subject: [PATCH 1/2] Remove unused `destroyed_surface_ids` --- winit/src/platform_specific/wayland/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/winit/src/platform_specific/wayland/mod.rs b/winit/src/platform_specific/wayland/mod.rs index 315bda5ed0..ef4b5a1d79 100644 --- a/winit/src/platform_specific/wayland/mod.rs +++ b/winit/src/platform_specific/wayland/mod.rs @@ -62,7 +62,6 @@ pub(crate) struct WaylandSpecific { display_handle: Option, modifiers: Modifiers, surface_ids: HashMap, - destroyed_surface_ids: HashMap, subsurface_state: Option, surface_subsurfaces: HashMap>, } @@ -136,7 +135,6 @@ impl WaylandSpecific { sender, display_handle, surface_ids, - destroyed_surface_ids, modifiers, subsurface_state, surface_subsurfaces, From 58a2d7a53af6b368ee868e0d3772ff912c363aa7 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 17 Dec 2024 15:13:24 -0800 Subject: [PATCH 2/2] winit/wayland: 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. This re-uses `Connection` in the platform-specific code, instead of creating from display handle on each call. --- Cargo.toml | 7 +- winit/Cargo.toml | 2 + winit/src/platform_specific/mod.rs | 43 ++++++------ winit/src/platform_specific/wayland/mod.rs | 70 +++++++++++++++++++ .../wayland/subsurface_widget.rs | 48 ++++++++++++- winit/src/program.rs | 42 ++++++++--- 6 files changed, 174 insertions(+), 38 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ca5c62c343..66af816647 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" @@ -222,9 +223,9 @@ wayland-protocols = { version = "0.32.1", features = ["staging"] } winapi = "0.3" # window_clipboard = "0.4.1" -window_clipboard = { git = "https://github.com/pop-os/window_clipboard.git", tag = "pop-0.13" } -dnd = { git = "https://github.com/pop-os/window_clipboard.git", tag = "pop-0.13" } -mime = { git = "https://github.com/pop-os/window_clipboard.git", tag = "pop-0.13" } +window_clipboard = { git = "https://github.com/pop-os/window_clipboard.git", tag = "pop-0.13-2" } +dnd = { git = "https://github.com/pop-os/window_clipboard.git", tag = "pop-0.13-2" } +mime = { git = "https://github.com/pop-os/window_clipboard.git", tag = "pop-0.13-2" } winit = { git = "https://github.com/pop-os/winit.git", tag = "iced-xdg-surface-0.13" } # winit = { path = "../../winit" } # winit = { git = "https://github.com/iced-rs/winit.git", rev = "254d6b3420ce4e674f516f7a2bd440665e05484d" } 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..5490bfac77 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,31 +79,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; }; @@ -137,6 +119,21 @@ 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 type UserInterfaces<'a, P> = HashMap< diff --git a/winit/src/platform_specific/wayland/mod.rs b/winit/src/platform_specific/wayland/mod.rs index ef4b5a1d79..1b3b8cf5b8 100644 --- a/winit/src/platform_specific/wayland/mod.rs +++ b/winit/src/platform_specific/wayland/mod.rs @@ -17,10 +17,13 @@ use iced_futures::futures::channel::mpsc; use iced_graphics::Compositor; use iced_runtime::core::window; use iced_runtime::Debug; +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; use winit::window::CursorIcon; @@ -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,28 @@ 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 +137,10 @@ impl PlatformSpecific { } impl WaylandSpecific { + pub(crate) fn conn(&self) -> Option<&Connection> { + self.conn.as_ref() + } + pub(crate) fn handle_event<'a, P, C>( &mut self, e: SctkEvent, @@ -134,6 +164,7 @@ impl WaylandSpecific { proxy, sender, display_handle, + conn, surface_ids, modifiers, subsurface_state, @@ -198,4 +229,43 @@ 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::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 fb3ff9ac09..5cde0e96bb 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,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 @@ -572,6 +617,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..f3b1ae9db7 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -796,6 +796,8 @@ async fn run_instance<'a, P, C>( let mut cur_dnd_surface: Option = None; + let mut dnd_surface: Option>> = None; + debug.startup_finished(); loop { // Empty the queue if possible @@ -964,9 +966,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,11 +977,25 @@ 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() { + // TODO Remove id + let id = window::Id::unique(); + platform_specific_handler + .update_subsurfaces(id, &surface); + platform_specific_handler.update_surface_shm(&surface, viewport.physical_width(), viewport.physical_height(), &bytes); + let surface = Arc::new(surface); + dnd_surface = Some(surface.clone()); + Icon::Surface(dnd::DndSurface(surface)) + } else { + platform_specific_handler + .clear_subsurface_list(); + Icon::Buffer { + data: Arc::new(bytes), + width: viewport.physical_width(), + height: viewport.physical_height(), + transparent: true, + } } }, ); @@ -1191,7 +1205,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 +1290,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 @@ -1723,7 +1737,13 @@ async fn run_instance<'a, P, C>( 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()))); }