iced-yoda/winit/src/platform_specific/wayland/subsurface_widget.rs
Votre Nom e424487704
Some checks are pending
Audit / vulnerabilities (push) Waiting to run
Check / wasm (push) Waiting to run
Check / widget (push) Waiting to run
Document / all (push) Waiting to run
Format / all (push) Waiting to run
Lint / all (push) Waiting to run
Test / all (macOS-latest, 1.88) (push) Waiting to run
Test / all (macOS-latest, beta) (push) Waiting to run
Test / all (macOS-latest, stable) (push) Waiting to run
Test / all (ubuntu-latest, 1.88) (push) Waiting to run
Test / all (ubuntu-latest, beta) (push) Waiting to run
Test / all (ubuntu-latest, stable) (push) Waiting to run
Test / all (windows-latest, 1.88) (push) Waiting to run
Test / all (windows-latest, beta) (push) Waiting to run
Test / all (windows-latest, stable) (push) Waiting to run
yoda: iced_winit dead-code purge (29→0 warnings)
Removed code paths inherited from past refactors that no longer have any
reader:

- ViewFn type alias (window.rs)
- a11y_enabled flag and resized flag (lib.rs) — assigned but never read
- BootConfig.fonts/graphics_settings/is_wayland — already passed to
  run_instance(...) directly; the BootConfig copies were never read
- Runner.system_theme oneshot::Sender — stored but no producer ever
  wired; receiver side already falls back to default
- event_loop/control_flow.rs — local ControlFlow enum, all callers use
  winit::event_loop::ControlFlow
- event_loop/proxy.rs — Proxy<Message> never constructed
- Error::Connect(ConnectError) variant — never constructed
- Common.has_focus / ime_pos / ime_size — never read after assignment
- SctkPopupData.grab — replicated settings.grab, never read back
- SubsurfaceEventVariant::Created.surface field — destructure does
  `surface: _` everywhere, the value is unused

Visibility tightening:
- a11y.rs WinitActivationHandler / WinitActionHandler /
  WinitDeactivationHandler: pub proxy → pub(crate) proxy
- conversion::touch_event: pub → private (no external callers)
- proxy::Proxy:🆕 pub → pub(crate)

Allow attributes (intentional, not warning-suppression):
- conversion::RawImage: #[allow(dead_code)] — fields kept for
  IconProvider downcast-via-AsAny on winit's side
- event_loop::Error: #[allow(dead_code)] — variant payloads kept for
  Debug, never inspected programmatically
- lib.rs Event<Message>: #[allow(hidden_glob_reexports)] — intentional
  shadow of winit::event::Event coming from `pub use winit`
- window.rs/lib.rs: #[allow(deprecated)] on enable_ime/disable_ime/
  process_event with TODOs for set_ime_* → request_ime_update and
  try_next → try_recv migrations

Five orphan imports also removed (Hash/Hasher, BorrowMut, 3× Compositor).

Leyoda 2026 – GPLv3
2026-05-05 18:26:08 +02:00

924 lines
28 KiB
Rust

// TODO z-order option?
use crate::core::{
ContentFit, Element, Length, Rectangle, Size, Vector,
layout::{self, Layout},
mouse, renderer,
widget::{self, Widget},
};
use std::{
cell::RefCell,
collections::HashMap,
fmt::Debug,
future::Future,
hash::{Hash, Hasher},
mem,
os::unix::io::{AsFd, OwnedFd},
pin::Pin,
ptr,
sync::{Arc, Mutex, Weak},
task,
};
use crate::futures::futures::channel::oneshot;
use cctk::sctk::{
compositor::SurfaceData,
error::GlobalError,
globals::{GlobalData, ProvidesBoundGlobal},
reexports::client::{
Connection, Dispatch, Proxy, QueueHandle, delegate_noop,
protocol::{
wl_buffer::{self, WlBuffer},
wl_compositor::WlCompositor,
wl_output,
wl_shm::{self, WlShm},
wl_shm_pool::{self, WlShmPool},
wl_subcompositor::WlSubcompositor,
wl_subsurface::WlSubsurface,
wl_surface::WlSurface,
},
},
shm::slot::SlotPool,
};
use iced_futures::core::window;
use wayland_protocols::wp::{
alpha_modifier::v1::client::{
wp_alpha_modifier_surface_v1::WpAlphaModifierSurfaceV1,
wp_alpha_modifier_v1::WpAlphaModifierV1,
},
fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1,
linux_dmabuf::zv1::client::{
zwp_linux_buffer_params_v1::{self, ZwpLinuxBufferParamsV1},
zwp_linux_dmabuf_v1::{self, ZwpLinuxDmabufV1},
},
viewporter::client::{
wp_viewport::WpViewport, wp_viewporter::WpViewporter,
},
};
use winit::window::WindowId;
use crate::platform_specific::event_loop::state::SctkState;
#[derive(Debug)]
pub struct Plane {
pub fd: OwnedFd,
pub plane_idx: u32,
pub offset: u32,
pub stride: u32,
}
#[derive(Debug)]
pub struct Dmabuf {
pub width: i32,
pub height: i32,
pub planes: Vec<Plane>,
pub format: u32,
pub modifier: u64,
}
#[derive(Debug)]
pub struct Shmbuf {
pub fd: OwnedFd,
pub offset: i32,
pub width: i32,
pub height: i32,
pub stride: i32,
pub format: wl_shm::Format,
}
#[derive(Debug)]
pub enum BufferSource {
Shm(Shmbuf),
Dma(Dmabuf),
}
impl From<Shmbuf> for BufferSource {
fn from(buf: Shmbuf) -> Self {
Self::Shm(buf)
}
}
impl From<Dmabuf> for BufferSource {
fn from(buf: Dmabuf) -> Self {
Self::Dma(buf)
}
}
#[derive(Debug)]
struct SubsurfaceBufferInner {
source: Arc<BufferSource>,
_sender: oneshot::Sender<()>,
}
/// Refcounted type containing a `BufferSource` with a sender that is signaled
/// all references are dropped and `wl_buffer`s created from the source are
/// released.
#[derive(Clone, Debug)]
pub struct SubsurfaceBuffer(Arc<SubsurfaceBufferInner>);
pub struct BufferData {
source: WeakBufferSource,
// This reference is held until the surface `release`s the buffer
subsurface_buffer: Mutex<Option<SubsurfaceBuffer>>,
}
impl BufferData {
fn for_buffer(buffer: &WlBuffer) -> Option<&Self> {
buffer.data::<BufferData>()
}
}
/// Future signalled when subsurface buffer is released
pub struct SubsurfaceBufferRelease(oneshot::Receiver<()>);
impl SubsurfaceBufferRelease {
/// Non-blocking check if buffer is released yet, without awaiting
pub fn released(&mut self) -> bool {
self.0.try_recv() == Ok(None)
}
}
impl Future for SubsurfaceBufferRelease {
type Output = ();
fn poll(
mut self: Pin<&mut Self>,
cx: &mut task::Context<'_>,
) -> task::Poll<()> {
Pin::new(&mut self.0).poll(cx).map(|_| ())
}
}
impl SubsurfaceBuffer {
pub fn new(source: Arc<BufferSource>) -> (Self, SubsurfaceBufferRelease) {
let (_sender, receiver) = oneshot::channel();
let subsurface_buffer =
SubsurfaceBuffer(Arc::new(SubsurfaceBufferInner {
source,
_sender,
}));
(subsurface_buffer, SubsurfaceBufferRelease(receiver))
}
pub fn width(&self) -> i32 {
match &*self.0.source {
BufferSource::Dma(dma) => dma.width,
BufferSource::Shm(shm) => shm.width,
}
}
pub fn height(&self) -> i32 {
match &*self.0.source {
BufferSource::Dma(dma) => dma.height,
BufferSource::Shm(shm) => shm.height,
}
}
// Behavior of `wl_buffer::released` is undefined if attached to multiple surfaces. To allow
// things like that, create a new `wl_buffer` each time.
fn create_buffer(
&self,
shm: &WlShm,
dmabuf: Option<&ZwpLinuxDmabufV1>,
qh: &QueueHandle<SctkState>,
) -> Option<WlBuffer> {
// create reference to source, that is dropped on release
match self.0.source.as_ref() {
BufferSource::Shm(buf) => {
let pool = shm.create_pool(
buf.fd.as_fd(),
buf.offset + buf.height * buf.stride,
qh,
GlobalData,
);
let buffer = pool.create_buffer(
buf.offset,
buf.width,
buf.height,
buf.stride,
buf.format,
qh,
BufferData {
source: WeakBufferSource(Arc::downgrade(
&self.0.source,
)),
subsurface_buffer: Mutex::new(Some(self.clone())),
},
);
pool.destroy();
Some(buffer)
}
BufferSource::Dma(buf) => {
if let Some(dmabuf) = dmabuf {
let params = dmabuf.create_params(qh, GlobalData);
for plane in &buf.planes {
let modifier_hi = (buf.modifier >> 32) as u32;
let modifier_lo = (buf.modifier & 0xffffffff) as u32;
params.add(
plane.fd.as_fd(),
plane.plane_idx,
plane.offset,
plane.stride,
modifier_hi,
modifier_lo,
);
}
// Will cause protocol error if format is not supported
Some(params.create_immed(
buf.width,
buf.height,
buf.format,
zwp_linux_buffer_params_v1::Flags::empty(),
qh,
BufferData {
source: WeakBufferSource(Arc::downgrade(
&self.0.source,
)),
subsurface_buffer: Mutex::new(Some(self.clone())),
},
))
} else {
None
}
}
}
}
}
impl PartialEq for SubsurfaceBuffer {
fn eq(&self, rhs: &Self) -> bool {
Arc::ptr_eq(&self.0, &rhs.0)
}
}
impl Dispatch<WlShmPool, GlobalData> for SctkState {
fn event(
_: &mut SctkState,
_: &WlShmPool,
_: wl_shm_pool::Event,
_: &GlobalData,
_: &Connection,
_: &QueueHandle<SctkState>,
) {
unreachable!()
}
}
impl Dispatch<ZwpLinuxDmabufV1, GlobalData> for SctkState {
fn event(
_: &mut SctkState,
_: &ZwpLinuxDmabufV1,
_: zwp_linux_dmabuf_v1::Event,
_: &GlobalData,
_: &Connection,
_: &QueueHandle<SctkState>,
) {
}
}
impl Dispatch<ZwpLinuxBufferParamsV1, GlobalData> for SctkState {
fn event(
_: &mut SctkState,
_: &ZwpLinuxBufferParamsV1,
_: zwp_linux_buffer_params_v1::Event,
_: &GlobalData,
_: &Connection,
_: &QueueHandle<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 {
fn event(
_: &mut SctkState,
_: &WlBuffer,
event: wl_buffer::Event,
data: &BufferData,
_: &Connection,
_: &QueueHandle<SctkState>,
) {
match event {
wl_buffer::Event::Release => {
// Release reference to `SubsurfaceBuffer`
_ = data.subsurface_buffer.lock().unwrap().take();
}
_ => unreachable!(),
}
}
}
#[doc(hidden)]
#[derive(Clone, Debug)]
pub(crate) struct WeakBufferSource(Weak<BufferSource>);
impl PartialEq for WeakBufferSource {
fn eq(&self, rhs: &Self) -> bool {
Weak::ptr_eq(&self.0, &rhs.0)
}
}
impl Eq for WeakBufferSource {}
impl Hash for WeakBufferSource {
fn hash<H: Hasher>(&self, state: &mut H) {
ptr::hash::<BufferSource, _>(self.0.as_ptr(), state)
}
}
// 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?)
// release
#[derive(Debug, Clone)]
#[doc(hidden)]
pub struct SubsurfaceState {
pub wl_compositor: WlCompositor,
pub wl_subcompositor: WlSubcompositor,
pub wp_viewporter: WpViewporter,
pub wl_shm: WlShm,
pub wp_dmabuf: Option<ZwpLinuxDmabufV1>,
pub wp_alpha_modifier: Option<WpAlphaModifierV1>,
pub qh: QueueHandle<SctkState>,
pub(crate) buffers: HashMap<WeakBufferSource, Vec<WlBuffer>>,
pub(crate) unmapped_subsurfaces: Vec<SubsurfaceInstance>,
pub new_iced_subsurfaces: Vec<(
window::Id,
WlSurface,
window::Id,
WlSubsurface,
WlSurface,
i32,
)>,
}
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,
scale: f64,
data: &[u8],
offset: Vector,
) {
let wp_viewport = self.wp_viewporter.get_viewport(
&surface,
&self.qh,
cctk::sctk::globals::GlobalData,
);
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);
if let Err(err) = buffer.attach_to(&surface) {
log::warn!("failed to attach shm buffer to subsurface: {err}");
return;
}
surface.offset(offset.x as i32, offset.y as i32);
wp_viewport.set_destination(
(width as f64 / scale) as i32,
(height as f64 / scale) as i32,
);
surface.commit();
wp_viewport.destroy();
}
fn create_subsurface(&self, parent: &WlSurface) -> SubsurfaceInstance {
let wl_surface = self
.wl_compositor
.create_surface(&self.qh, SurfaceData::new(None, 1));
// Use empty input region so parent surface gets pointer events
let region = self.wl_compositor.create_region(&self.qh, ());
wl_surface.set_input_region(Some(&region));
region.destroy();
let wl_subsurface = self.wl_subcompositor.get_subsurface(
&wl_surface,
parent,
&self.qh,
(),
);
let wp_viewport = self.wp_viewporter.get_viewport(
&wl_surface,
&self.qh,
cctk::sctk::globals::GlobalData,
);
let wp_alpha_modifier_surface =
self.wp_alpha_modifier.as_ref().map(|wp_alpha_modifier| {
wp_alpha_modifier.get_surface(&wl_surface, &self.qh, ())
});
SubsurfaceInstance {
wl_surface,
wl_subsurface,
wp_viewport,
wp_alpha_modifier_surface,
wl_buffer: None,
bounds: None,
wp_fractional_scale: None,
transform: wl_output::Transform::Normal,
z: 0,
parent: parent.clone(),
}
}
// Update `subsurfaces` from `view_subsurfaces`
pub(crate) fn update_subsurfaces(
&mut self,
parent: &WlSurface,
subsurfaces: &mut Vec<SubsurfaceInstance>,
view_subsurfaces: &[SubsurfaceInfo],
) {
// Subsurfaces aren't destroyed immediately to sync removal with parent
// surface commit. Since `destroy` is immediate.
//
// They should be safe to destroy by the next time `update_subsurfaces`
// is run.
ICED_SUBSURFACES.with_borrow_mut(|surfaces| {
surfaces.retain(|s| {
!self
.unmapped_subsurfaces
.iter()
.any(|unmapped| unmapped.wl_surface == s.4)
})
});
self.unmapped_subsurfaces.clear();
// Remove cached `wl_buffers` for any `BufferSource`s that no longer exist.
self.buffers.retain(|k, v| {
let retain = k.0.strong_count() > 0;
if !retain {
v.iter().for_each(|b| b.destroy());
}
retain
});
// If view requested fewer subsurfaces than there currently are,
// unmap excess.
while view_subsurfaces.len() < subsurfaces.len() {
let subsurface = subsurfaces.pop().unwrap();
subsurface.unmap();
self.unmapped_subsurfaces.push(subsurface);
}
let needs_sorting = subsurfaces.len() < view_subsurfaces.len()
|| !self.new_iced_subsurfaces.is_empty();
// Create new subsurfaces if there aren't enough.
while subsurfaces.len() < view_subsurfaces.len() {
subsurfaces.push(self.create_subsurface(parent));
}
if needs_sorting {
let mut sorted_subsurfaces: Vec<_> = view_subsurfaces
.iter()
.zip(subsurfaces.iter_mut())
.map(|(subsurface_info, instance)| {
(
instance.parent.clone(),
instance.wl_subsurface.clone(),
instance.wl_surface.clone(),
// Use from `view_subsurfaces`; not updated in `subsurfaces`
// until `attach_and_commit`
subsurface_info.z,
)
})
.chain(self.new_iced_subsurfaces.clone().into_iter().map(
|(_, parent, _, wl_subsurface, wl_surface, z)| {
(parent.clone(), wl_subsurface, wl_surface, z)
},
))
.chain(ICED_SUBSURFACES.with(|surfaces| {
let b = surfaces.borrow();
let v: Vec<_> = b
.iter()
.map(move |s| {
(s.1.clone(), s.3.clone(), s.4.clone(), s.5)
})
.collect();
v.into_iter()
}))
.collect();
sorted_subsurfaces.sort_by(|a, b| a.3.cmp(&b.3));
// Attach buffers to subsurfaces, set viewports, and commit.
'outer: for (i, subsurface) in sorted_subsurfaces.iter().enumerate()
{
for prev in sorted_subsurfaces[0..i].iter().rev() {
if prev.0 == subsurface.0 {
// Fist surface that has `z` greater than 0, so place above parent,
// rather than previous subsurface.
if prev.3 < 0 && subsurface.3 >= 0 {
subsurface.1.place_above(&subsurface.0);
} else {
subsurface.1.place_above(&prev.2);
}
continue 'outer;
}
}
// No previous surface with same parent
if subsurface.3 < 0 {
// Place below parent if z < 0
subsurface.1.place_below(&subsurface.0);
} else {
subsurface.1.place_above(&subsurface.0);
}
}
}
if !self.new_iced_subsurfaces.is_empty() {
ICED_SUBSURFACES.with(|surfaces| {
surfaces.borrow_mut().append(&mut self.new_iced_subsurfaces);
})
};
for (subsurface_data, subsurface) in
view_subsurfaces.iter().zip(subsurfaces.iter_mut())
{
subsurface.attach_and_commit(subsurface_data, self);
}
}
// Cache `wl_buffer` for use when `BufferSource` is used in future
// (Avoid creating wl_buffer each buffer swap)
fn insert_cached_wl_buffer(&mut self, buffer: WlBuffer) {
let source = BufferData::for_buffer(&buffer).unwrap().source.clone();
self.buffers.entry(source).or_default().push(buffer);
}
// Gets a cached `wl_buffer` for the `SubsurfaceBuffer`, if any. And stores `SubsurfaceBuffer`
// reference to be releated on `wl_buffer` release.
//
// If `wl_buffer` isn't released, it is destroyed instead.
fn get_cached_wl_buffer(
&mut self,
subsurface_buffer: &SubsurfaceBuffer,
) -> Option<WlBuffer> {
let buffers = self.buffers.get_mut(&WeakBufferSource(
Arc::downgrade(&subsurface_buffer.0.source),
))?;
while let Some(buffer) = buffers.pop() {
let mut subsurface_buffer_ref = buffer
.data::<BufferData>()
.unwrap()
.subsurface_buffer
.lock()
.unwrap();
if subsurface_buffer_ref.is_none() {
*subsurface_buffer_ref = Some(subsurface_buffer.clone());
drop(subsurface_buffer_ref);
return Some(buffer);
} else {
buffer.destroy();
}
}
None
}
}
impl Drop for SubsurfaceState {
fn drop(&mut self) {
self.buffers
.values()
.flatten()
.for_each(|buffer| buffer.destroy());
}
}
#[derive(Clone, Debug)]
pub struct SubsurfaceInstance {
pub(crate) wl_surface: WlSurface,
pub(crate) wl_subsurface: WlSubsurface,
pub(crate) wp_viewport: WpViewport,
pub(crate) wp_fractional_scale: Option<WpFractionalScaleV1>,
pub(crate) wp_alpha_modifier_surface: Option<WpAlphaModifierSurfaceV1>,
pub(crate) wl_buffer: Option<WlBuffer>,
pub(crate) bounds: Option<Rectangle<f32>>,
pub(crate) transform: wl_output::Transform,
pub(crate) z: i32,
pub parent: WlSurface,
}
impl SubsurfaceInstance {
// TODO correct damage? no damage/commit if unchanged?
fn attach_and_commit(
&mut self,
info: &SubsurfaceInfo,
state: &mut SubsurfaceState,
) {
let buffer_changed;
let old_buffer = self.wl_buffer.take();
let old_buffer_data =
old_buffer.as_ref().and_then(|b| BufferData::for_buffer(&b));
let buffer = if old_buffer_data.is_some_and(|b| {
b.subsurface_buffer.lock().unwrap().as_ref() == Some(&info.buffer)
}) {
// Same "BufferSource" is already attached to this subsurface. Don't create new `wl_buffer`.
buffer_changed = false;
old_buffer.unwrap()
} else {
if let Some(old_buffer) = old_buffer {
state.insert_cached_wl_buffer(old_buffer);
}
buffer_changed = true;
if let Some(buffer) = state.get_cached_wl_buffer(&info.buffer) {
buffer
} else if let Some(buffer) = info.buffer.create_buffer(
&state.wl_shm,
state.wp_dmabuf.as_ref(),
&state.qh,
) {
buffer
} else {
// TODO log error
self.wl_surface.attach(None, 0, 0);
return;
}
};
// XXX scale factor?
let bounds_changed = self.bounds != Some(info.bounds);
// wlroots seems to have issues changing buffer without running this
if bounds_changed || buffer_changed {
self.wl_subsurface
.set_position(info.bounds.x as i32, info.bounds.y as i32);
self.wp_viewport.set_destination(
info.bounds.width as i32,
info.bounds.height as i32,
);
}
let transform_changed = self.transform != info.transform;
if transform_changed {
self.wl_surface.set_buffer_transform(info.transform);
}
if buffer_changed {
self.wl_surface.attach(Some(&buffer), 0, 0);
self.wl_surface.damage(0, 0, i32::MAX, i32::MAX);
}
if buffer_changed || bounds_changed || transform_changed {
_ = self.wl_surface.frame(&state.qh, self.wl_surface.clone());
self.wl_surface.commit();
}
if let Some(wp_alpha_modifier_surface) = &self.wp_alpha_modifier_surface
{
let alpha = (info.alpha.clamp(0.0, 1.0) * u32::MAX as f32) as u32;
wp_alpha_modifier_surface.set_multiplier(alpha);
}
self.wl_buffer = Some(buffer);
self.bounds = Some(info.bounds);
self.transform = info.transform;
self.z = info.z;
}
pub fn unmap(&self) {
self.wl_surface.attach(None, 0, 0);
self.wl_surface.commit();
}
}
impl Drop for SubsurfaceInstance {
fn drop(&mut self) {
self.wp_viewport.destroy();
self.wl_subsurface.destroy();
self.wl_surface.destroy();
if let Some(wl_buffer) = self.wl_buffer.as_ref() {
wl_buffer.destroy();
}
}
}
#[derive(Debug)]
pub(crate) struct SubsurfaceInfo {
pub buffer: SubsurfaceBuffer,
pub bounds: Rectangle<f32>,
pub alpha: f32,
pub transform: wl_output::Transform,
pub z: i32,
}
thread_local! {
static SUBSURFACES: RefCell<Vec<SubsurfaceInfo>> = RefCell::new(Vec::new());
static ICED_SUBSURFACES: RefCell<Vec<(window::Id, WlSurface, window::Id, WlSubsurface, WlSurface, i32)>> = RefCell::new(Vec::new());
}
pub(crate) fn take_subsurfaces() -> Vec<SubsurfaceInfo> {
SUBSURFACES.with(|subsurfaces| mem::take(&mut *subsurfaces.borrow_mut()))
}
pub(crate) fn is_subsurface(id: WindowId) -> bool {
ICED_SUBSURFACES.with(|subsurfaces| {
subsurfaces.borrow_mut().iter().any(|s| {
winit::window::WindowId::from_raw(s.4.id().as_ptr() as usize) == id
})
})
}
pub(crate) fn subsurface_ids(parent: WindowId) -> Vec<WindowId> {
ICED_SUBSURFACES.with(|subsurfaces| {
subsurfaces
.borrow_mut()
.iter()
.filter_map(|s| {
if winit::window::WindowId::from_raw(s.1.id().as_ptr() as usize)
== parent
{
Some(winit::window::WindowId::from_raw(
s.4.id().as_ptr() as usize
))
} else {
None
}
})
.collect()
})
}
pub(crate) fn remove_iced_subsurface(surface: &WlSurface) {
ICED_SUBSURFACES.with(|surfaces| {
surfaces
.borrow_mut()
.retain(|(_, _, _, _, s, _)| s != surface)
})
}
#[must_use]
pub struct Subsurface {
buffer: SubsurfaceBuffer,
width: Length,
height: Length,
content_fit: ContentFit,
alpha: f32,
transform: wl_output::Transform,
pub z: i32,
}
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer> for Subsurface
where
Renderer: renderer::Renderer,
{
fn size(&self) -> Size<Length> {
Size::new(self.width, self.height)
}
// Based on image widget
fn layout(
&mut self,
_tree: &mut widget::Tree,
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let (width, height) = match self.transform {
wl_output::Transform::_90
| wl_output::Transform::_270
| wl_output::Transform::Flipped90
| wl_output::Transform::Flipped270 => {
(self.buffer.height(), self.buffer.width())
}
_ => (self.buffer.width(), self.buffer.height()),
};
let buffer_size = Size::new(width as f32, height as f32);
// TODO apply transform
let raw_size = limits.resolve(self.width, self.height, buffer_size);
let full_size = self.content_fit.fit(buffer_size, raw_size);
let final_size = Size {
width: match self.width {
Length::Shrink => f32::min(raw_size.width, full_size.width),
_ => raw_size.width,
},
height: match self.height {
Length::Shrink => f32::min(raw_size.height, full_size.height),
_ => raw_size.height,
},
};
layout::Node::new(final_size)
}
fn draw(
&self,
_state: &widget::Tree,
_renderer: &mut Renderer,
_theme: &Theme,
_style: &renderer::Style,
layout: Layout<'_>,
_cursor: mouse::Cursor,
_viewport: &Rectangle,
) {
// Instead of using renderer, we need to add surface to a list that is
// read by the iced-sctk shell.
SUBSURFACES.with(|subsurfaces| {
subsurfaces.borrow_mut().push(SubsurfaceInfo {
buffer: self.buffer.clone(),
bounds: layout.bounds(),
alpha: self.alpha,
transform: self.transform,
z: self.z,
})
});
}
}
impl Subsurface {
pub fn new(buffer: SubsurfaceBuffer) -> Self {
Self {
buffer,
// Matches defaults of image widget
width: Length::Shrink,
height: Length::Shrink,
content_fit: ContentFit::Contain,
alpha: 1.,
transform: wl_output::Transform::Normal,
z: 0,
}
}
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
}
pub fn content_fit(mut self, content_fit: ContentFit) -> Self {
self.content_fit = content_fit;
self
}
pub fn alpha(mut self, alpha: f32) -> Self {
self.alpha = alpha;
self
}
/// If `z` is less than 0, it will be below the main surface
pub fn z(mut self, z: i32) -> Self {
self.z = z;
self
}
pub fn transform(mut self, transform: wl_output::Transform) -> Self {
self.transform = transform;
self
}
}
impl<Message, Theme, Renderer> From<Subsurface>
for Element<'static, Message, Theme, Renderer>
where
Message: Clone,
Renderer: renderer::Renderer,
{
fn from(subsurface: Subsurface) -> Self {
Self::new(subsurface)
}
}
delegate_noop!(SctkState: ignore WpAlphaModifierV1);
delegate_noop!(SctkState: ignore WpAlphaModifierSurfaceV1);