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:
parent
545ecb8a8a
commit
fde0689def
6 changed files with 190 additions and 26 deletions
|
|
@ -296,6 +296,7 @@ wgpu = { version = "27.0", default-features = false, features = [
|
||||||
"wgsl",
|
"wgsl",
|
||||||
] }
|
] }
|
||||||
wayland-protocols = { version = "0.32.1", features = ["staging"] }
|
wayland-protocols = { version = "0.32.1", features = ["staging"] }
|
||||||
|
wayland-client = { version = "0.31.5" }
|
||||||
# web-time = "1.1"
|
# web-time = "1.1"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -73,12 +73,14 @@ cctk.workspace = true
|
||||||
cctk.optional = true
|
cctk.optional = true
|
||||||
wayland-protocols.workspace = true
|
wayland-protocols.workspace = true
|
||||||
wayland-protocols.optional = true
|
wayland-protocols.optional = true
|
||||||
|
wayland-client.workspace = true
|
||||||
wayland-backend = { version = "0.3.1", features = [
|
wayland-backend = { version = "0.3.1", features = [
|
||||||
"client_system",
|
"client_system",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
xkbcommon = { version = "0.7", features = ["wayland"], optional = true }
|
xkbcommon = { version = "0.7", features = ["wayland"], optional = true }
|
||||||
xkbcommon-dl = { version = "0.4.1", optional = true }
|
xkbcommon-dl = { version = "0.4.1", optional = true }
|
||||||
xkeysym = { version = "0.2.0", optional = true }
|
xkeysym = { version = "0.2.0", optional = true }
|
||||||
|
rustix = { version = "0.38" }
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
winapi.workspace = true
|
winapi.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ pub use iced_program as program;
|
||||||
pub use program::core;
|
pub use program::core;
|
||||||
pub use program::graphics;
|
pub use program::graphics;
|
||||||
pub use program::runtime;
|
pub use program::runtime;
|
||||||
|
use raw_window_handle::HasWindowHandle;
|
||||||
pub use runtime::futures;
|
pub use runtime::futures;
|
||||||
use window_clipboard::mime::ClipboardStoreData;
|
use window_clipboard::mime::ClipboardStoreData;
|
||||||
pub use winit;
|
pub use winit;
|
||||||
|
|
@ -657,6 +658,10 @@ async fn run_instance<P>(
|
||||||
let mut clipboard = Clipboard::unconnected();
|
let mut clipboard = Clipboard::unconnected();
|
||||||
let mut cur_dnd_surface: Option<window::Id> = None;
|
let mut cur_dnd_surface: Option<window::Id> = None;
|
||||||
|
|
||||||
|
let mut dnd_surface: Option<
|
||||||
|
Arc<Box<dyn HasWindowHandle + Send + Sync + 'static>>,
|
||||||
|
> = None;
|
||||||
|
|
||||||
#[cfg(feature = "a11y")]
|
#[cfg(feature = "a11y")]
|
||||||
let (mut adapters, mut a11y_enabled) = (Default::default(), false);
|
let (mut adapters, mut a11y_enabled) = (Default::default(), false);
|
||||||
// let (mut adapters, mut a11y_enabled) = if let Some((main_id, title, raw)) =
|
// let (mut adapters, mut a11y_enabled) = if let Some((main_id, title, raw)) =
|
||||||
|
|
@ -1133,7 +1138,8 @@ async fn run_instance<P>(
|
||||||
},
|
},
|
||||||
cursor,
|
cursor,
|
||||||
);
|
);
|
||||||
platform_specific_handler.clear_subsurface_list();
|
platform_specific_handler
|
||||||
|
.update_subsurfaces(id, window.raw.rwh_06_window_handle());
|
||||||
draw_span.finish();
|
draw_span.finish();
|
||||||
|
|
||||||
if let user_interface::State::Updated {
|
if let user_interface::State::Updated {
|
||||||
|
|
@ -1508,7 +1514,14 @@ async fn run_instance<P>(
|
||||||
dnd::DndEvent::Offer(..) => {
|
dnd::DndEvent::Offer(..) => {
|
||||||
events.push((cur_dnd_surface, core::Event::Dnd(e)));
|
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() {
|
for w in window_manager.ids() {
|
||||||
events.push((Some(w), core::Event::Dnd(e.clone())));
|
events.push((Some(w), core::Event::Dnd(e.clone())));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use std::collections::HashMap;
|
||||||
use cctk::sctk::reexports::client::Connection;
|
use cctk::sctk::reexports::client::Connection;
|
||||||
use iced_graphics::{Compositor, compositor};
|
use iced_graphics::{Compositor, compositor};
|
||||||
use iced_runtime::{core::window, platform_specific, user_interface};
|
use iced_runtime::{core::window, platform_specific, user_interface};
|
||||||
|
use raw_window_handle::HasWindowHandle;
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||||
pub mod wayland;
|
pub mod wayland;
|
||||||
|
|
@ -82,7 +83,7 @@ impl PlatformSpecific {
|
||||||
pub(crate) fn update_subsurfaces(
|
pub(crate) fn update_subsurfaces(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: window::Id,
|
id: window::Id,
|
||||||
window: &dyn winit::window::Window,
|
window: &dyn HasWindowHandle,
|
||||||
) {
|
) {
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||||
{
|
{
|
||||||
|
|
@ -91,31 +92,12 @@ impl PlatformSpecific {
|
||||||
};
|
};
|
||||||
use wayland_backend::client::ObjectId;
|
use wayland_backend::client::ObjectId;
|
||||||
|
|
||||||
let Ok(backend) = window.rwh_06_display_handle().display_handle()
|
let Some(conn) = self.wayland.conn() else {
|
||||||
else {
|
log::error!("No Wayland conn");
|
||||||
log::error!("No display handle");
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let conn = match backend.as_raw() {
|
let Ok(raw) = window.window_handle() else {
|
||||||
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 {
|
|
||||||
log::error!("Invalid window handle {id:?}");
|
log::error!("Invalid window handle {id:?}");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -150,6 +132,31 @@ impl PlatformSpecific {
|
||||||
self.wayland.update_subsurfaces(id, &wl_surface);
|
self.wayland.update_subsurfaces(id, &wl_surface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn create_surface(
|
||||||
|
&mut self,
|
||||||
|
) -> Option<Box<dyn HasWindowHandle + Send + Sync + 'static>> {
|
||||||
|
#[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>(
|
pub(crate) fn handle_event<'a, P>(
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,13 @@ use cursor_icon::CursorIcon;
|
||||||
use iced_futures::futures::channel::mpsc;
|
use iced_futures::futures::channel::mpsc;
|
||||||
use iced_graphics::{Compositor, compositor};
|
use iced_graphics::{Compositor, compositor};
|
||||||
use iced_runtime::core::window;
|
use iced_runtime::core::window;
|
||||||
|
use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle};
|
||||||
|
use raw_window_handle::{HasRawDisplayHandle, RawWindowHandle};
|
||||||
use sctk_event::SctkEvent;
|
use sctk_event::SctkEvent;
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
use subsurface_widget::{SubsurfaceInstance, SubsurfaceState};
|
use subsurface_widget::{SubsurfaceInstance, SubsurfaceState};
|
||||||
use wayland_backend::client::ObjectId;
|
use wayland_backend::client::ObjectId;
|
||||||
|
use wayland_client::{Connection, Proxy};
|
||||||
use winit::event_loop::OwnedDisplayHandle;
|
use winit::event_loop::OwnedDisplayHandle;
|
||||||
|
|
||||||
pub(crate) enum Action {
|
pub(crate) enum Action {
|
||||||
|
|
@ -60,6 +63,7 @@ pub(crate) struct WaylandSpecific {
|
||||||
proxy: Option<winit::event_loop::EventLoopProxy>,
|
proxy: Option<winit::event_loop::EventLoopProxy>,
|
||||||
sender: Option<calloop::channel::Sender<Action>>,
|
sender: Option<calloop::channel::Sender<Action>>,
|
||||||
display_handle: Option<OwnedDisplayHandle>,
|
display_handle: Option<OwnedDisplayHandle>,
|
||||||
|
conn: Option<Connection>,
|
||||||
modifiers: Modifiers,
|
modifiers: Modifiers,
|
||||||
surface_ids: HashMap<ObjectId, SurfaceIdWrapper>,
|
surface_ids: HashMap<ObjectId, SurfaceIdWrapper>,
|
||||||
subsurface_state: Option<SubsurfaceState>,
|
subsurface_state: Option<SubsurfaceState>,
|
||||||
|
|
@ -74,6 +78,26 @@ impl PlatformSpecific {
|
||||||
display: OwnedDisplayHandle,
|
display: OwnedDisplayHandle,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
self.wayland.winit_event_sender = Some(tx);
|
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.display_handle = Some(display);
|
||||||
self.wayland.proxy = Some(raw);
|
self.wayland.proxy = Some(raw);
|
||||||
// TODO remove this
|
// TODO remove this
|
||||||
|
|
@ -111,6 +135,10 @@ impl PlatformSpecific {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WaylandSpecific {
|
impl WaylandSpecific {
|
||||||
|
pub(crate) fn conn(&self) -> Option<&Connection> {
|
||||||
|
self.conn.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn handle_event<'a, P>(
|
pub(crate) fn handle_event<'a, P>(
|
||||||
&mut self,
|
&mut self,
|
||||||
e: SctkEvent,
|
e: SctkEvent,
|
||||||
|
|
@ -135,6 +163,7 @@ impl WaylandSpecific {
|
||||||
proxy,
|
proxy,
|
||||||
sender,
|
sender,
|
||||||
display_handle,
|
display_handle,
|
||||||
|
conn,
|
||||||
surface_ids,
|
surface_ids,
|
||||||
modifiers,
|
modifiers,
|
||||||
subsurface_state,
|
subsurface_state,
|
||||||
|
|
@ -198,4 +227,70 @@ impl WaylandSpecific {
|
||||||
&subsurfaces,
|
&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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,9 @@ use std::{
|
||||||
use crate::futures::futures::channel::oneshot;
|
use crate::futures::futures::channel::oneshot;
|
||||||
use cctk::sctk::{
|
use cctk::sctk::{
|
||||||
compositor::SurfaceData,
|
compositor::SurfaceData,
|
||||||
globals::GlobalData,
|
error::GlobalError,
|
||||||
|
globals::{GlobalData, ProvidesBoundGlobal},
|
||||||
|
shm::slot::SlotPool,
|
||||||
reexports::client::{
|
reexports::client::{
|
||||||
Connection, Dispatch, Proxy, QueueHandle, delegate_noop,
|
Connection, Dispatch, Proxy, QueueHandle, delegate_noop,
|
||||||
protocol::{
|
protocol::{
|
||||||
|
|
@ -250,6 +252,7 @@ impl PartialEq for SubsurfaceBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Dispatch<WlShmPool, GlobalData> for SctkState {
|
impl Dispatch<WlShmPool, GlobalData> for SctkState {
|
||||||
fn event(
|
fn event(
|
||||||
_: &mut SctkState,
|
_: &mut SctkState,
|
||||||
|
|
@ -287,6 +290,23 @@ impl Dispatch<ZwpLinuxBufferParamsV1, GlobalData> for SctkState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Dispatch<WlBuffer, GlobalData> for SctkState {
|
||||||
|
fn event(
|
||||||
|
_: &mut SctkState,
|
||||||
|
_: &WlBuffer,
|
||||||
|
event: wl_buffer::Event,
|
||||||
|
_: &GlobalData,
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<SctkState>,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
wl_buffer::Event::Release => {
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Dispatch<WlBuffer, BufferData> for SctkState {
|
impl Dispatch<WlBuffer, BufferData> for SctkState {
|
||||||
fn event(
|
fn event(
|
||||||
_: &mut SctkState,
|
_: &mut SctkState,
|
||||||
|
|
@ -324,6 +344,15 @@ impl Hash for WeakBufferSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implement `ProvidesBoundGlobal` to use `SlotPool`
|
||||||
|
struct ShmGlobal<'a>(&'a WlShm);
|
||||||
|
|
||||||
|
impl<'a> ProvidesBoundGlobal<WlShm, 1> for ShmGlobal<'a> {
|
||||||
|
fn bound_global(&self) -> Result<WlShm, GlobalError> {
|
||||||
|
Ok(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// create wl_buffer from BufferSource (avoid create_immed?)
|
// create wl_buffer from BufferSource (avoid create_immed?)
|
||||||
// release
|
// release
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -341,6 +370,22 @@ pub struct SubsurfaceState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
fn create_subsurface(&self, parent: &WlSurface) -> SubsurfaceInstance {
|
||||||
let wl_surface = self
|
let wl_surface = self
|
||||||
.wl_compositor
|
.wl_compositor
|
||||||
|
|
@ -571,6 +616,7 @@ impl Drop for SubsurfaceInstance {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub(crate) struct SubsurfaceInfo {
|
pub(crate) struct SubsurfaceInfo {
|
||||||
pub buffer: SubsurfaceBuffer,
|
pub buffer: SubsurfaceBuffer,
|
||||||
pub bounds: Rectangle<f32>,
|
pub bounds: Rectangle<f32>,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue