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.
This commit is contained in:
Ian Douglas Scott 2024-12-17 15:13:24 -08:00 committed by Ashley Wulber
parent 545ecb8a8a
commit fde0689def
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
6 changed files with 190 additions and 26 deletions

View file

@ -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<winit::event_loop::EventLoopProxy>,
sender: Option<calloop::channel::Sender<Action>>,
display_handle: Option<OwnedDisplayHandle>,
conn: Option<Connection>,
modifiers: Modifiers,
surface_ids: HashMap<ObjectId, SurfaceIdWrapper>,
subsurface_state: Option<SubsurfaceState>,
@ -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<Box<dyn HasWindowHandle + Send + Sync + 'static>> {
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();
}
}