diff --git a/Cargo.lock b/Cargo.lock index c28424ee..b309db1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3206,6 +3206,7 @@ dependencies = [ "resvg", "rustc-hash 2.1.2", "rustix 0.38.44", + "smithay-client-toolkit 0.20.0", "thiserror 2.0.18", "tiny-xlib", "wayland-backend", diff --git a/graphics/src/image.rs b/graphics/src/image.rs index 8ef868cd..d9b23216 100644 --- a/graphics/src/image.rs +++ b/graphics/src/image.rs @@ -1,8 +1,6 @@ //! Load and operate on images. #[cfg(feature = "image")] use crate::core::Bytes; -#[cfg(feature = "image")] -use crate::core::Size; use crate::core::Rectangle; use crate::core::image; @@ -180,105 +178,3 @@ fn to_error(error: ::image::ImageError) -> image::Error { error => image::Error::Invalid(Arc::new(error)), } } - -/// resample a raster image to this target -#[cfg(feature = "image")] -pub fn downsample_target( - native: Size, - bounds: Size, -) -> Option> { - if !(bounds.width >= 1.0 && bounds.height >= 1.0) { - return None; - } - - // Round up to 4px so an animated resize does not make a copy per pixel - // TODO: maybe find a better approach - let quantize = |length: f32| (length.ceil() as u32).div_ceil(4) * 4; - - let target = Size::new( - quantize(bounds.width).min(native.width), - quantize(bounds.height).min(native.height), - ); - - let minified = native.width as f32 >= target.width as f32 * 1.25 - || native.height as f32 >= target.height as f32 * 1.25; - - // If the target is big then Lanczos will be too expensive, and sampler is good enough - let small = u64::from(target.width) * u64::from(target.height) <= 1 << 18; - - (minified && small).then_some(target) -} - -/// Resamples premultiplied pixels down to `target`. -#[cfg(feature = "image")] -pub fn downsample_premultiplied( - pixels: &[u8], - size: Size, - target: Size, -) -> Vec { - let image = - ::image::RgbaImage::from_raw(size.width, size.height, pixels.to_vec()) - .expect("pixels hold width * height RGBA pixels"); - - resize(&image, target).into_raw() -} - -#[cfg(feature = "image")] -fn resize(image: &::image::RgbaImage, target: Size) -> ::image::RgbaImage { - use ::image::imageops::{self, FilterType}; - use std::borrow::Cow; - - // if image is too big compared to target, box average it to double the target - // and then do Lanczos. The final result is almost the same, but lanczos is too - // expensive on large images. - let image = if image.width() >= target.width * 4 - && image.height() >= target.height * 4 - { - Cow::Owned(imageops::thumbnail( - image, - target.width * 2, - target.height * 2, - )) - } else { - Cow::Borrowed(image) - }; - - imageops::resize(&*image, target.width, target.height, FilterType::Lanczos3) -} - -/// Resamples RGBA pixels down to `target`. -/// -/// Premultiplies the image, to avoid fringing the edges of an icon when interpolating -/// transparent pixels. -#[cfg(feature = "image")] -pub fn downsample(image: &Buffer, target: Size) -> ::image::RgbaImage { - let mut image = ::image::RgbaImage::from_raw( - image.width(), - image.height(), - image.as_raw().to_vec(), - ) - .expect("buffer holds width * height RGBA pixels"); - - for pixel in image.pixels_mut() { - let alpha = u32::from(pixel[3]); - - for channel in &mut pixel.0[..3] { - *channel = ((u32::from(*channel) * alpha + 127) / 255) as u8; - } - } - - let mut image = resize(&image, target); - - for pixel in image.pixels_mut() { - let alpha = u32::from(pixel[3]); - - if alpha > 0 { - for channel in &mut pixel.0[..3] { - *channel = ((u32::from(*channel) * 255 + alpha / 2) / alpha) - .min(255) as u8; - } - } - } - - image -} diff --git a/tiny_skia/src/raster.rs b/tiny_skia/src/raster.rs index 0535158a..7c820917 100644 --- a/tiny_skia/src/raster.rs +++ b/tiny_skia/src/raster.rs @@ -51,21 +51,7 @@ impl Pipeline { ) { let mut cache = self.cache.borrow_mut(); - let target = { - let Ok(image) = cache.allocate(handle) else { - return; - }; - - graphics::image::downsample_target( - Size::new(image.width(), image.height()), - bounds.size(), - ) - }; - - let Ok(mut image) = (match target { - Some(target) => cache.allocate_resampled(handle, target), - None => cache.allocate(handle), - }) else { + let Ok(mut image) = cache.allocate(handle) else { return; }; @@ -124,8 +110,6 @@ impl Pipeline { struct Cache { entries: FxHashMap>, hits: FxHashSet, - resampled: FxHashMap<(raster::Id, u32, u32), Entry>, - resampled_hits: FxHashSet<(raster::Id, u32, u32)>, } impl Cache { @@ -182,59 +166,9 @@ impl Cache { Ok(ret) } - /// Like [`Self::allocate`], resampled to `target`. Call after - /// [`Self::allocate`] has decoded the image. - pub fn allocate_resampled( - &mut self, - handle: &raster::Handle, - target: Size, - ) -> Result, raster::Error> { - let key = (handle.id(), target.width, target.height); - - if !self.resampled.contains_key(&key) { - let native = self - .entries - .get(&handle.id()) - .and_then(Option::as_ref) - .ok_or(raster::Error::Empty)?; - - // Stored pixels are already premultiplied, so resample them as is. - let pixels = graphics::image::downsample_premultiplied( - bytemuck::cast_slice(&native.pixels), - Size::new(native.width, native.height), - target, - ); - - let _ = self.resampled.insert( - key, - Entry { - width: target.width, - height: target.height, - pixels: pixels - .chunks_exact(4) - .map(|p| u32::from_ne_bytes([p[0], p[1], p[2], p[3]])) - .collect(), - }, - ); - } - - let _ = self.resampled_hits.insert(key); - let entry = &self.resampled[&key]; - - Ok(tiny_skia::PixmapRef::from_bytes( - bytemuck::cast_slice(&entry.pixels), - entry.width, - entry.height, - ) - .expect("Build pixmap from image bytes")) - } - fn trim(&mut self) { self.entries.retain(|key, _| self.hits.contains(key)); - self.resampled - .retain(|key, _| self.resampled_hits.contains(key)); self.hits.clear(); - self.resampled_hits.clear(); } } diff --git a/wgpu/src/image/cache.rs b/wgpu/src/image/cache.rs index 37f1e33d..88a48640 100644 --- a/wgpu/src/image/cache.rs +++ b/wgpu/src/image/cache.rs @@ -5,8 +5,6 @@ use crate::image::atlas::{self, Atlas}; #[cfg(all(feature = "image", not(target_arch = "wasm32")))] use worker::Worker; -#[cfg(feature = "image")] -use rustc_hash::{FxHashMap, FxHashSet}; #[cfg(feature = "image")] use std::collections::HashMap; @@ -40,9 +38,6 @@ impl Cache { raster: Raster { cache: crate::image::raster::Cache::default(), pending: HashMap::new(), - resampled: FxHashMap::default(), - resampled_hits: FxHashSet::default(), - should_trim: false, belt: wgpu::util::StagingBelt::new( device.clone(), 2 * 1024 * 1024, @@ -214,9 +209,7 @@ impl Cache { encoder: &mut wgpu::CommandEncoder, belt: &mut wgpu::util::StagingBelt, handle: &core::image::Handle, - bounds: Size, ) -> Option<(&atlas::Entry, &Arc)> { - use crate::graphics::image::{downsample, downsample_target, load}; use crate::image::raster::Memory; self.receive(); @@ -230,34 +223,6 @@ impl Cache { None, )?; - if let Some(target) = downsample_target(memory.dimensions(), bounds) { - let key = (handle.id(), target.width, target.height); - - if !self.raster.resampled.contains_key(&key) { - let image = memory.host().or_else(|| load(handle).ok())?; - let image = downsample(&image, target); - - let entry = self.atlas.upload( - device, - encoder, - belt, - target.width, - target.height, - &image, - )?; - - let _ = self.raster.resampled.insert(key, entry); - self.raster.should_trim = true; - } - - let _ = self.raster.resampled_hits.insert(key); - - return Some(( - self.raster.resampled.get(&key)?, - self.atlas.bind_group(), - )); - } - if let Memory::Device { entry, bind_group, .. } = memory @@ -338,23 +303,6 @@ impl Cache { #[cfg(not(target_arch = "wasm32"))] self.worker.drop(_bind_group); }); - - if self.raster.should_trim { - let hits = &self.raster.resampled_hits; - let atlas = &mut self.atlas; - - self.raster.resampled.retain(|key, entry| { - let retain = hits.contains(key); - - if !retain { - atlas.remove(entry); - } - - retain - }); - self.raster.resampled_hits.clear(); - self.raster.should_trim = false; - } } #[cfg(feature = "svg")] @@ -428,9 +376,6 @@ impl Drop for Cache { struct Raster { cache: crate::image::raster::Cache, pending: HashMap>, - resampled: FxHashMap<(core::image::Id, u32, u32), atlas::Entry>, - resampled_hits: FxHashSet<(core::image::Id, u32, u32)>, - should_trim: bool, belt: wgpu::util::StagingBelt, } diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index 2b5f7ade..d0665ee4 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -267,13 +267,7 @@ impl State { clip_bounds, } => { if let Some((atlas_entry, bind_group)) = cache - .upload_raster( - device, - encoder, - belt, - &image.handle, - bounds.size() * scale, - ) + .upload_raster(device, encoder, belt, &image.handle) { match atlas.as_mut() { None => { diff --git a/winit/src/platform_specific/wayland/commands/layer_surface.rs b/winit/src/platform_specific/wayland/commands/layer_surface.rs index 5d8a4588..cdb4e93d 100644 --- a/winit/src/platform_specific/wayland/commands/layer_surface.rs +++ b/winit/src/platform_specific/wayland/commands/layer_surface.rs @@ -1,6 +1,5 @@ //! Interact with the window of your application. -use crate::core::Rectangle; use crate::core::window::Id as SurfaceId; use iced_runtime::{ self, Action, Task, @@ -126,15 +125,3 @@ pub fn set_padding( )), )) } - -/// -pub fn set_input_zone( - id: SurfaceId, - zone: Option>, -) -> Task { - task::effect(Action::PlatformSpecific( - platform_specific::Action::Wayland(wayland::Action::LayerSurface( - wayland::layer_surface::Action::InputZone { id, zone }, - )), - )) -} diff --git a/winit/src/platform_specific/wayland/event_loop/mod.rs b/winit/src/platform_specific/wayland/event_loop/mod.rs index 1926e391..b4589840 100644 --- a/winit/src/platform_specific/wayland/event_loop/mod.rs +++ b/winit/src/platform_specific/wayland/event_loop/mod.rs @@ -1,5 +1,4 @@ pub mod control_flow; -pub mod popup; pub mod proxy; pub mod state; @@ -7,7 +6,6 @@ pub mod state; use crate::platform_specific::SurfaceIdWrapper; use crate::{ Control, - event_loop::popup::PopupManager, futures::futures::channel::mpsc, handlers::{ ext_background_effect, overlap::OverlapNotifyV1, @@ -179,41 +177,13 @@ impl SctkEventLoop { } } crate::platform_specific::Action::SetCursor( - surface, icon, ) => { - state.record_cursor(surface.clone(), |r| { - r.icon = Some(icon); - r.hidden = false; - }); - if let Some(seat) = state - .seats - .get_mut(0) - .filter(|s| s.is_over(&surface)) - { + if let Some(seat) = state.seats.get_mut(0) { seat.icon = Some(icon); - seat.hidden = false; seat.set_cursor(&state.connection, icon); } } - crate::platform_specific::Action::SetCursorVisible( - surface, - visible, - ) => { - state.record_cursor(surface.clone(), |r| { - r.hidden = !visible; - }); - if let Some(seat) = state - .seats - .get_mut(0) - .filter(|s| s.is_over(&surface)) - { - seat.set_cursor_visible( - &state.connection, - visible, - ); - } - } crate::platform_specific::Action::RequestRedraw( id, ) => { @@ -407,13 +377,12 @@ impl SctkEventLoop { blur_surfaces: HashMap::new(), corner_radii: HashMap::new(), layer_surfaces: Vec::new(), - popmgr: PopupManager::default(), + popups: Vec::new(), lock_surfaces: Vec::new(), subsurfaces: Vec::new(), touch_points: HashMap::new(), sctk_events: Vec::new(), frame_status: HashMap::new(), - cursor_requests: HashMap::new(), fractional_scaling_manager, viewporter_state, compositor_updates: Default::default(), @@ -545,8 +514,8 @@ impl SctkEventLoop { .chain( state .state - .popmgr - .popups() + .popups + .iter() .map(|s| s.popup.wl_surface()), ) .chain( diff --git a/winit/src/platform_specific/wayland/event_loop/popup.rs b/winit/src/platform_specific/wayland/event_loop/popup.rs deleted file mode 100644 index 109e9af6..00000000 --- a/winit/src/platform_specific/wayland/event_loop/popup.rs +++ /dev/null @@ -1,189 +0,0 @@ -use std::sync::Mutex; -use std::sync::Arc; -use cctk::sctk::shell::xdg::popup::PopupConfigure; -use cctk::sctk::shell::xdg::popup::Popup; -use iced_runtime::{platform_specific, core::window::Id}; -use wayland_client::protocol::wl_surface::WlSurface; -use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1; - -use crate::event_loop::state::Common; -use crate::event_loop::state::SctkPopupData; - -#[derive(Debug, Default)] -pub struct PopupManager { - chains: Vec>, -} - -impl PopupManager { - pub(crate) fn popups(&self) -> impl Iterator { - self.chains.iter().map(|c| c.iter()).flatten() - } - - pub(crate) fn chain_for_popup_mut( - &mut self, - id: &WlSurface, - ) -> Option<(&mut Vec, usize)> { - let pos = self.chains.iter().enumerate().find_map(move |(pos, c)| { - if let Some(pop_pos) = - c.into_iter().position(|p| p.popup.wl_surface() == id) - { - Some((pos, pop_pos)) - } else { - None - } - }); - - pos.map(|pos| (&mut self.chains[pos.0], pos.1)) - } - - pub(crate) fn chain_for_popup( - &self, - id: &WlSurface, - ) -> Option<(&Vec, usize)> { - let pos = self.chains.iter().enumerate().find_map(move |(pos, c)| { - if let Some(pop_pos) = - c.into_iter().position(|p| p.popup.wl_surface() == id) - { - Some((pos, pop_pos)) - } else { - None - } - }); - - pos.map(|pos| (&self.chains[pos.0], pos.1)) - } - - pub(crate) fn popup(&self, id: &WlSurface) -> Option<&SctkPopup> { - self.chains.iter().find_map(move |c| { - c.into_iter().find(|p| p.popup.wl_surface() == id) - }) - } - - pub(crate) fn popup_mut( - &mut self, - id: &WlSurface, - ) -> Option<&mut SctkPopup> { - self.chains.iter_mut().find_map(move |c| { - c.into_iter().find(|p| p.popup.wl_surface() == id) - }) - } - - pub(crate) fn popup_id(&self, id: Id) -> Option<&SctkPopup> { - self.chains - .iter() - .find_map(move |c| c.into_iter().find(|p| p.data.id == id)) - } - - pub(crate) fn popup_id_mut(&mut self, id: Id) -> Option<&mut SctkPopup> { - self.chains - .iter_mut() - .find_map(move |c| c.into_iter().find(|p| p.data.id == id)) - } - - pub(crate) fn active_grab(&self) -> Option<&SctkPopup> { - self.chains - .iter() - .find_map(|c| c.into_iter().rev().find(|p| p.data.grab)) - } - - pub(crate) fn root_grab(&self) -> Option<&SctkPopup> { - self.chains - .iter() - .find_map(|c| c.into_iter().find(|p| p.data.grab)) - } - - pub(crate) fn push(&mut self, popup: SctkPopup) { - if let Some((chain, _pos)) = - self.chain_for_popup_mut(popup.data.parent.wl_surface()) - { - // TODO should we return an error if a popup is attempted to be added to a non-leaf popup of a chain? - chain.push(popup); - } else { - if let Some(empty) = self.chains.iter_mut().find(|c| c.is_empty()) { - empty.push(popup); - } else { - self.chains.push(vec![popup]); - } - } - } - - pub(crate) fn remove( - &mut self, - popup: &WlSurface, - ) -> Option> { - // must perform cleanup so that the popups are dropped in the correct order - let ret = if let Some((chain, mut pos)) = - self.chain_for_popup_mut(popup) - { - // TODO should we return an error if a popup is attempted to be added to a non-leaf popup of a chain? - while let Some(p) = pos.checked_sub(1).and_then(|p| chain.get(p)) { - if p.close_with_children { - pos -= 1; - } else { - break; - } - } - - Some(chain.drain(pos..).rev()) - } else { - None - }; - - return ret; - } - - pub(crate) fn remove_ignore_children( - &mut self, - popup: &WlSurface, - ) -> Option> { - // must perform cleanup so that the popups are dropped in the correct order - let ret = if let Some((chain, pos)) = self.chain_for_popup_mut(popup) { - Some(chain.drain(pos..).rev()) - } else { - None - }; - - return ret; - } -} - -#[derive(Debug)] -pub struct SctkPopup { - pub(crate) popup: Popup, - pub(crate) last_configure: Option, - pub(crate) _pending_requests: - Vec, - pub(crate) data: SctkPopupData, - pub(crate) common: Arc>, - pub(crate) wp_fractional_scale: Option, - pub(crate) close_with_children: bool, -} - -impl SctkPopup { - pub(crate) fn set_size(&mut self, w: u32, h: u32, token: u32) { - let guard = self.common.lock().unwrap(); - if guard.size.width == w && guard.size.height == h { - return; - } - drop(guard); - // update geometry - self.popup - .xdg_surface() - .set_window_geometry(0, 0, w as i32, h as i32); - self.update_viewport(w, h); - // update positioner - self.data.positioner.set_size(w as i32, h as i32); - self.popup.reposition(&self.data.positioner, token); - } - - pub(crate) fn update_viewport(&mut self, w: u32, h: u32) { - let common = self.common.lock().unwrap(); - if common.size.width == w && common.size.height == h { - return; - } - if let Some(viewport) = common.wp_viewport.as_ref() { - // Set inner size without the borders. - viewport.set_destination(w as i32, h as i32); - } - } -} diff --git a/winit/src/platform_specific/wayland/event_loop/state.rs b/winit/src/platform_specific/wayland/event_loop/state.rs index 80914ffb..f4a8c5d7 100644 --- a/winit/src/platform_specific/wayland/event_loop/state.rs +++ b/winit/src/platform_specific/wayland/event_loop/state.rs @@ -1,6 +1,5 @@ use crate::{ Control, - event_loop::popup::{self, PopupManager}, handlers::{ activation::IcedRequestData, ext_background_effect, @@ -83,7 +82,10 @@ use cctk::{ Anchor, KeyboardInteractivity, Layer, LayerShell, LayerSurface, LayerSurfaceConfigure, SurfaceKind, }, - xdg::{XdgPositioner, XdgShell, popup::Popup}, + xdg::{ + XdgPositioner, XdgShell, + popup::{Popup, PopupConfigure}, + }, }, shm::{Shm, multi::MultiPool}, }, @@ -92,7 +94,6 @@ use cctk::{ }; use iced_runtime::{ core::{self, Point, touch}, - keyboard::key::Named::Settings, platform_specific::{ self, wayland::{ @@ -122,13 +123,6 @@ use wayland_protocols::{ pub static TOKEN_CTR: AtomicU32 = AtomicU32::new(0); -/// The requested cursor for a surface -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub(crate) struct CursorRequest { - pub(crate) icon: Option, - pub(crate) hidden: bool, -} - #[derive(Debug)] pub(crate) struct SctkSeat { pub(crate) seat: WlSeat, @@ -145,8 +139,6 @@ pub(crate) struct SctkSeat { pub(crate) active_icon: Option, // Cursor icon set by application pub(crate) icon: Option, - // Application asked for cursor to hide - pub(crate) hidden: bool, } impl SctkSeat { @@ -156,35 +148,6 @@ impl SctkSeat { self.active_icon = Some(icon); } } - - /// Is the pointer currently over `surface`. - pub(crate) fn is_over(&self, surface: &ObjectId) -> bool { - self.ptr_focus.as_ref().is_some_and(|s| &s.id() == surface) - } - - /// Set cursor visibility over this surfaces. - pub(crate) fn set_cursor_visible( - &mut self, - conn: &Connection, - visible: bool, - ) { - let Some(ptr) = self.ptr.as_ref() else { - return; - }; - - self.hidden = !visible; - - if visible { - let icon = self.icon.unwrap_or(CursorIcon::Default); - _ = ptr.set_cursor(conn, icon); - self.active_icon = Some(icon); - } else { - // Ignoring the error - // It only fails when the pointer is not on our surface (due to serial error) - _ = ptr.hide_cursor(); - self.active_icon = None; - } - } } #[derive(Debug, Clone)] @@ -300,6 +263,47 @@ impl From> for Common { } } +#[derive(Debug)] +pub struct SctkPopup { + pub(crate) popup: Popup, + pub(crate) last_configure: Option, + pub(crate) _pending_requests: + Vec, + pub(crate) data: SctkPopupData, + pub(crate) common: Arc>, + pub(crate) wp_fractional_scale: Option, + pub(crate) close_with_children: bool, +} + +impl SctkPopup { + pub(crate) fn set_size(&mut self, w: u32, h: u32, token: u32) { + let guard = self.common.lock().unwrap(); + if guard.size.width == w && guard.size.height == h { + return; + } + drop(guard); + // update geometry + self.popup + .xdg_surface() + .set_window_geometry(0, 0, w as i32, h as i32); + self.update_viewport(w, h); + // update positioner + self.data.positioner.set_size(w as i32, h as i32); + self.popup.reposition(&self.data.positioner, token); + } + + pub(crate) fn update_viewport(&mut self, w: u32, h: u32) { + let common = self.common.lock().unwrap(); + if common.size.width == w && common.size.height == h { + return; + } + if let Some(viewport) = common.wp_viewport.as_ref() { + // Set inner size without the borders. + viewport.set_destination(w as i32, h as i32); + } + } +} + #[derive(Debug)] pub struct SctkLockSurface { pub(crate) id: core::window::Id, @@ -333,7 +337,6 @@ pub struct SctkSubsurface { pub struct SctkPopupData { pub(crate) id: core::window::Id, pub(crate) parent: PopupParent, - pub(crate) parent_window: core::window::Id, pub(crate) toplevel: WlSurface, pub(crate) positioner: Arc, pub(crate) grab: bool, @@ -428,7 +431,7 @@ pub struct SctkState { /// `WindowUpdate` or buffer on the associated with it `WindowHandle`. pub(crate) windows: Vec, pub(crate) layer_surfaces: Vec, - pub(crate) popmgr: PopupManager, + pub(crate) popups: Vec, pub(crate) subsurfaces: Vec, pub(crate) lock_surfaces: Vec, pub(crate) blur_surfaces: HashMap, @@ -468,7 +471,6 @@ pub struct SctkState { pub(crate) session_lock_state: SessionLockState, pub(crate) session_lock: Option, pub(crate) id_map: HashMap, - pub(crate) cursor_requests: HashMap, pub(crate) to_commit: HashMap, pub(crate) destroyed: HashSet, pub(crate) pending_popup: Option<(SctkPopupSettings, usize)>, @@ -571,17 +573,6 @@ pub(crate) fn receive_frame( } impl SctkState { - /// What a surface wants the pointer over it to be. - pub(crate) fn record_cursor( - &mut self, - surface: ObjectId, - amend: impl FnOnce(&mut CursorRequest), - ) { - self.cursor_requests - .retain(|id, _| id == &surface || self.id_map.contains_key(id)); - amend(self.cursor_requests.entry(surface).or_default()); - } - pub fn request_redraw(&mut self, surface: &WlSurface) { let e = self .frame_status @@ -618,7 +609,11 @@ impl SctkState { } } - if let Some(popup) = self.popmgr.popup(surface) { + if let Some(popup) = self + .popups + .iter_mut() + .find(|p| p.popup.wl_surface() == surface) + { id = Some(popup.data.id); if legacy && popup.wp_fractional_scale.is_some() { return; @@ -694,7 +689,7 @@ impl SctkState { > { if self.layer_surfaces.iter().any(|s| s.id == settings.id) || self.windows.iter().any(|w| w.id == settings.id) - || self.popmgr.popup_id(settings.id).is_some() + || self.popups.iter().any(|p| p.data.id == settings.id) || self.subsurfaces.iter().any(|s| s.id == settings.id) { log::warn!( @@ -718,7 +713,12 @@ impl SctkState { PopupParent::Window(parent.wl_surface(&self.connection)), parent.wl_surface(&self.connection), ) - } else if let Some(parent) = self.popmgr.popup_id(settings.parent) { + } else if let Some(i) = self + .popups + .iter() + .position(|p| p.data.id == settings.parent) + { + let parent = &self.popups[i]; ( PopupParent::Popup(parent.popup.wl_surface().clone()), parent.data.toplevel.clone(), @@ -756,7 +756,6 @@ impl SctkState { if positioner.version() >= 3 && settings.positioner.reactive { positioner.set_reactive(); } - positioner.set_size(size.0 as i32, size.1 as i32); let grab = settings.grab; @@ -821,9 +820,10 @@ impl SctkState { ) } PopupParent::Popup(parent) => { - let Some(parent_xdg) = - self.popmgr.popup(parent).map(|p| p.popup.xdg_surface()) - else { + let Some(parent_xdg) = self.popups.iter().find_map(|p| { + (p.popup.wl_surface() == parent) + .then(|| p.popup.xdg_surface()) + }) else { return Err(PopupCreationError::ParentMissing); }; @@ -898,12 +898,11 @@ impl SctkState { let common = Arc::new(Mutex::new(common)); let positioner = Arc::new(positioner); - self.popmgr.push(popup::SctkPopup { + self.popups.push(SctkPopup { popup: popup.clone(), data: SctkPopupData { id: settings.id, parent: parent.clone(), - parent_window: settings.parent, toplevel: toplevel.clone(), positioner: positioner.clone(), grab: settings.grab, @@ -958,7 +957,7 @@ impl SctkState { if self.layer_surfaces.iter().any(|s| s.id == id) || self.windows.iter().any(|w| w.id == id) - || self.popmgr.popup_id(id).is_some() + || self.popups.iter().any(|p| p.data.id == id) || self.subsurfaces.iter().any(|s| s.id == id) { log::warn!("Layer surface with id {id:?} already exists"); @@ -1072,7 +1071,7 @@ impl SctkState { ) -> Option<(CommonSurface, Arc>)> { if self.layer_surfaces.iter().any(|s| s.id == id) || self.windows.iter().any(|w| w.id == id) - || self.popmgr.popup_id(id).is_some() + || self.popups.iter().any(|p| p.data.id == id) || self.subsurfaces.iter().any(|s| s.id == id) { log::warn!("Lock surface with id {id:?} already exists"); @@ -1151,9 +1150,12 @@ impl SctkState { } }, platform_specific::wayland::layer_surface::Action::Destroy(id) => { - if let Some(id) = self.layer_surfaces.iter().find_map(|l| if l.id == id {self.popmgr.popups().find_map(|p| (p.data.parent.wl_surface() == l.surface.wl_surface()).then(|| p.data.id))} else {None}).clone() { - _ = self.handle_action(Action::Popup(platform_specific::wayland::popup::Action::Destroy { id })); - } + if let Some(p) = self + .popups + .iter().find_map(|p| + self.layer_surfaces.iter().find_map(|l| if l.id == id && l.surface.wl_surface() == p.data.parent.wl_surface() {Some(p.data.id)} else {None})) { + _ = self.handle_action(Action::Popup(platform_specific::wayland::popup::Action::Destroy { id: p })); + } if let Some(i) = self.layer_surfaces.iter().position(|l| l.id == id) { let l = self.layer_surfaces.remove(i); @@ -1267,200 +1269,245 @@ impl SctkState { } }, }, - Action::Popup(action) => { - match action { - platform_specific::wayland::popup::Action::Popup { popup: settings } => { - // first check existing popup - // if an existing popup with the same id exists, we just update the positioner. - if let Some((chain, existing)) = self.popmgr.popup_id(settings.id).and_then(|p| - (p.data.grab == settings.grab && self.popmgr.popups().any(|parent| parent.popup.wl_surface() == p.data.parent.wl_surface() && parent.data.id == settings.parent) - || self.windows.iter().any(|w| w.id == settings.parent && *p.data.parent.wl_surface() == w.wl_surface(&self.connection)) - || self.layer_surfaces.iter().any(|l| l.id == settings.parent && p.data.parent.wl_surface() == l.surface.wl_surface())).then(|| - { - p.popup.wl_surface().clone() - })).and_then(|s| self.popmgr.chain_for_popup_mut(&s)) - { - let existing = &mut chain[existing]; - let size = if let Some(size) = settings.positioner.size { - size - } else { - let guard = existing.common.lock().unwrap(); - (guard.size.width, guard.size.height) + Action::Popup(action) => match action { + platform_specific::wayland::popup::Action::Popup { popup: settings } => { + // first check existing popup + if let Some(existing) = self.popups.iter().position(|p| p.data.id == settings.id + && ( + self.popups.iter().any(|parent| parent.popup.wl_surface() == p.data.parent.wl_surface() && parent.data.id == settings.parent) + || self.windows.iter().any(|w| w.id == settings.parent && *p.data.parent.wl_surface() == w.wl_surface(&self.connection)) + || self.layer_surfaces.iter().any(|l| l.id == settings.parent && p.data.parent.wl_surface() == l.surface.wl_surface())) + ) { + let existing = &mut self.popups[existing]; + let size = if let Some(size) = settings.positioner.size { + size + } else { + let guard = existing.common.lock().unwrap(); + (guard.size.width, guard.size.height) + }; + let Ok(positioner) = XdgPositioner::new(&self.xdg_shell_state) + .map_err(PopupCreationError::PositionerCreationFailed) else { + log::error!("Failed to create popup positioner"); + return Ok(()); }; - let Ok(positioner) = XdgPositioner::new(&self.xdg_shell_state) - .map_err(PopupCreationError::PositionerCreationFailed) else { - log::error!("Failed to create popup positioner"); - return Ok(()); - }; - positioner.set_anchor(settings.positioner.anchor); - positioner.set_anchor_rect( - settings.positioner.anchor_rect.x, - settings.positioner.anchor_rect.y, - settings.positioner.anchor_rect.width, - settings.positioner.anchor_rect.height, - ); - if let Ok(constraint_adjustment) = - settings.positioner.constraint_adjustment.try_into() - { - positioner.set_constraint_adjustment(constraint_adjustment); - } - positioner.set_gravity(settings.positioner.gravity); - positioner.set_offset( - settings.positioner.offset.0, - settings.positioner.offset.1, - ); - if settings.positioner.reactive { - positioner.set_reactive(); - } - positioner.set_size(size.0 as i32, size.1 as i32); - existing.data.positioner = Arc::new(positioner); - existing.set_size(size.0, size.1, TOKEN_CTR.fetch_add(1, std::sync::atomic::Ordering::Relaxed)); - _ = send_event(&self.events_sender, &self.proxy, - SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Size(size.0, size.1), toplevel_id: existing.data.parent.wl_surface().clone(), parent_id: existing.data.parent.wl_surface().clone(), id: existing.popup.wl_surface().clone(), parent_window: existing.data.parent_window }); - return Ok(()); - } else if !self.destroyed.is_empty() || self.popmgr.popup_id(settings.id).is_some() || self.popmgr.active_grab().is_some() + positioner.set_anchor(settings.positioner.anchor); + positioner.set_anchor_rect( + settings.positioner.anchor_rect.x, + settings.positioner.anchor_rect.y, + settings.positioner.anchor_rect.width, + settings.positioner.anchor_rect.height, + ); + if let Ok(constraint_adjustment) = + settings.positioner.constraint_adjustment.try_into() { - // must clean up popup chains so that there is just one grab chain... - let to_destroy = if settings.grab { - if let Some((grab_chain, p_i)) = self.popmgr.active_grab().and_then(|g| self.popmgr.chain_for_popup(g.popup.wl_surface())) { + positioner.set_constraint_adjustment(constraint_adjustment); + } + positioner.set_gravity(settings.positioner.gravity); + positioner.set_offset( + settings.positioner.offset.0, + settings.positioner.offset.1, + ); + if settings.positioner.reactive { + positioner.set_reactive(); + } + positioner.set_size(size.0 as i32, size.1 as i32); + existing.data.positioner = Arc::new(positioner); + existing.set_size(size.0, size.1, TOKEN_CTR.fetch_add(1, std::sync::atomic::Ordering::Relaxed)); + _ = send_event(&self.events_sender, &self.proxy, + SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Size(size.0, size.1), toplevel_id: existing.data.parent.wl_surface().clone(), parent_id: existing.data.parent.wl_surface().clone(), id: existing.popup.wl_surface().clone() }); + return Ok(()); + } + let mut parent_mismatch = !self.popups.is_empty(); + for p in &self.popups { + parent_mismatch = p.data.id != settings.parent; + } + if !self.destroyed.is_empty() || parent_mismatch { + if parent_mismatch { + let mut found = false; + for p in std::mem::take(&mut self.popups).into_iter().rev() { + let id = p.data.id; + self.popups.insert(0, p); - if let Some(pos) = grab_chain.iter().position(|p| p.data.id == settings.parent) { - // destroy children of parent - grab_chain.get(pos + 1).map(|g| g.data.id) + found |= id == settings.parent; + if !found { + _ = self.handle_action(Action::Popup(platform_specific::wayland::popup::Action::Destroy{id})); + } + + } + } + if self.pending_popup.replace((settings, 0)).is_none() { + + let timer = cctk::sctk::reexports::calloop::timer::Timer::from_duration(Duration::from_millis(30)); + let queue_handle = self.queue_handle.clone(); + _ = self.loop_handle.insert_source(timer, move |_, _, state| { + let Some((mut popup, attempt)) = state.pending_popup.take() else { + return TimeoutAction::Drop; + }; + + if !state.destroyed.is_empty() || state.popups.last().is_some_and(|p| { + state.id_map.get(&p.popup.wl_surface().id()).map_or(true, |p| *p != popup.parent) + }) { + if attempt < 5 { + state.pending_popup = Some((popup, attempt+1)); + TimeoutAction::ToDuration(Duration::from_millis(30)) } else { - // destroy whole chain... - grab_chain.get(0).map(|g| g.data.id) - } - } else { - None - } - } else { - None - }; - if let Some(to_destroy) = to_destroy { - _ = self.destroy_popup(to_destroy, true); - } - - if self.popmgr.popup_id(settings.id).is_some() { - let _ = self.handle_action(Action::Popup(platform_specific::wayland::popup::Action::Destroy { id: settings.id })); - } - if self.pending_popup.replace((settings, 0)).is_none() { - let timer = cctk::sctk::reexports::calloop::timer::Timer::from_duration(Duration::from_millis(30)); - let queue_handle = self.queue_handle.clone(); - _ = self.loop_handle.insert_source(timer, move |_, _, state| { - let Some((settings, attempt)) = state.pending_popup.take() else { - return TimeoutAction::Drop; - }; - // check if the mismatch still exists... - if !state.destroyed.is_empty() || state.popmgr.popup_id(settings.id).is_some_and(|p| - state.popmgr.popups().any(|parent| parent.popup.wl_surface() == p.data.parent.wl_surface() && parent.data.id == settings.parent)) - { - if attempt < 5 { - state.pending_popup = Some((settings, attempt+1)); - TimeoutAction::ToDuration(Duration::from_millis(30)) - } - else { - TimeoutAction::Drop - } - } else { - let parent_window = settings.parent; - match state.get_popup(settings) { - Ok((id, parent_id, toplevel_id, surface, common)) => { - let wl_surface = surface.wl_surface().clone(); - receive_frame(&mut state.frame_status, &wl_surface); - send_event(&state.events_sender, &state.proxy, - SctkEvent::PopupEvent { - variant: crate::platform_specific::wayland::sctk_event::PopupEventVariant::Created(queue_handle.clone(), surface, id, common, state.connection.display()), - toplevel_id, parent_id, id: wl_surface, parent_window }); - } - Err(err) => { - log::error!("Failed to create popup. {err:?}"); - } - }; TimeoutAction::Drop } - }); - } - } else { - let parent_window = settings.parent; - match self.get_popup(settings) { - Ok((id, parent_id, toplevel_id, surface, common)) => { - let wl_surface = surface.wl_surface().clone(); - send_event(&self.events_sender, &self.proxy, - SctkEvent::PopupEvent { - variant: crate::platform_specific::wayland::sctk_event::PopupEventVariant::Created( - self.queue_handle.clone(), surface, id, common, self.connection.display() - ), - toplevel_id, parent_id, id: wl_surface, parent_window } - ); - } - Err(err) => { - log::error!("Failed to create popup. {err:?}"); + } else { + match state.get_popup(popup) { + Ok((id, parent_id, toplevel_id, surface, common)) => { + let wl_surface = surface.wl_surface().clone(); + receive_frame(&mut state.frame_status, &wl_surface); + send_event(&state.events_sender, &state.proxy, + SctkEvent::PopupEvent { + variant: crate::platform_specific::wayland::sctk_event::PopupEventVariant::Created(queue_handle.clone(), surface, id, common, state.connection.display()), + toplevel_id, parent_id, id: wl_surface }); + } + Err(err) => { + log::error!("Failed to create popup. {err:?}"); + } + }; + TimeoutAction::Drop } + }); + } + } else { + self.pending_popup = None; + match self.get_popup(settings) { + Ok((id, parent_id, toplevel_id, surface, common)) => { + let wl_surface = surface.wl_surface().clone(); + send_event(&self.events_sender, &self.proxy, + SctkEvent::PopupEvent { + variant: crate::platform_specific::wayland::sctk_event::PopupEventVariant::Created(self.queue_handle.clone(), surface, id, common, self.connection.display()), + toplevel_id, parent_id, id: wl_surface }); + } + Err(err) => { + log::error!("Failed to create popup. {err:?}"); } } - }, - // XXX popup destruction must be done carefully - // first destroy the uppermost popup, then work down to the requested popup - platform_specific::wayland::popup::Action::Destroy { id } => { - if let Some(value) = self.destroy_popup(id, false) { - return value; + } + }, + // XXX popup destruction must be done carefully + // first destroy the uppermost popup, then work down to the requested popup + platform_specific::wayland::popup::Action::Destroy { id } => { + let sctk_popup = match self + .popups + .iter() + .position(|s| s.data.id == id) + { + Some(p) => self.popups.remove(p), + None => { + log::info!("No popup to destroy"); + return Ok(()); + }, + }; + let mut to_destroy = vec![sctk_popup]; + + while let Some(popup_to_destroy_last) = to_destroy.last().and_then(|popup| self + .popups + .iter() + .position(|p| popup.data.parent.wl_surface() == p.popup.wl_surface() && p.close_with_children)) { + let popup_to_destroy_last = self.popups.remove(popup_to_destroy_last); + to_destroy.push(popup_to_destroy_last); + } + to_destroy.reverse(); + + while let Some(popup_to_destroy_first) = to_destroy.last().and_then(|popup| self + .popups + .iter() + .position(|p| p.data.parent.wl_surface() == popup.popup.wl_surface())) { + let popup_to_destroy_first = self.popups.remove(popup_to_destroy_first); + to_destroy.push(popup_to_destroy_first); + } + for popup in to_destroy.into_iter().rev() { + if let Some(id) = self.id_map.remove(&popup.popup.wl_surface().id()) { + _ = self.destroyed.insert(id); } - }, - platform_specific::wayland::popup::Action::Size { id, width, height } => { - if let Some(sctk_popup) = self - .popmgr.popup_id_mut(id) + + if let Some(blurred) = self.blur_surfaces.remove(&id) { + blurred.destroy(); + } + _ = self.corner_radii.remove(&id); + + + let (removed, remaining): (Vec<_>, Vec<_>) = self + .subsurfaces + .drain(..) + .partition(|s| { + s.instance.parent == *popup.popup.wl_surface() + }); + + self.subsurfaces = remaining; + for s in removed { - // update geometry - // update positioner - sctk_popup.set_size(width, height, TOKEN_CTR.fetch_add(1, std::sync::atomic::Ordering::Relaxed)); - let surface = sctk_popup.popup.wl_surface().clone(); - _ = send_event(&self.events_sender, &self.proxy, - SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Size(width, height), toplevel_id: sctk_popup.data.parent.wl_surface().clone(), parent_id: sctk_popup.data.parent.wl_surface().clone(), id: surface, parent_window: sctk_popup.data.parent_window }); + crate::subsurface_widget::remove_iced_subsurface( + &s.instance.wl_surface, + ); + send_event(&self.events_sender, &self.proxy, + SctkEvent::SubsurfaceEvent( crate::sctk_event::SubsurfaceEventVariant::Destroyed(s.instance) ) + ); } - }, - platform_specific::wayland::popup::Action::Reposition { id, positioner } => { - if let Some(sctk_popup) = self - .popmgr.popup_id_mut(id) + _ = send_event(&self.events_sender, &self.proxy, + SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Done, toplevel_id: popup.data.toplevel.clone(), parent_id: popup.data.parent.wl_surface().clone(), id: popup.popup.wl_surface().clone() }); + } + }, + platform_specific::wayland::popup::Action::Size { id, width, height } => { + if let Some(sctk_popup) = self + .popups + .iter_mut() + .find(|s| s.data.id == id) + { + // update geometry + // update positioner + sctk_popup.set_size(width, height, TOKEN_CTR.fetch_add(1, std::sync::atomic::Ordering::Relaxed)); + let surface = sctk_popup.popup.wl_surface().clone(); + _ = send_event(&self.events_sender, &self.proxy, + SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Size(width, height), toplevel_id: sctk_popup.data.parent.wl_surface().clone(), parent_id: sctk_popup.data.parent.wl_surface().clone(), id: surface }); + } + }, + platform_specific::wayland::popup::Action::Reposition { id, positioner } => { + if let Some(sctk_popup) = self + .popups + .iter_mut() + .find(|s| s.data.id == id) + { + sctk_popup.data.positioner.set_anchor(positioner.anchor); + sctk_popup.data.positioner.set_anchor_rect( + positioner.anchor_rect.x, + positioner.anchor_rect.y, + positioner.anchor_rect.width, + positioner.anchor_rect.height, + ); + if let Ok(constraint_adjustment) = + positioner.constraint_adjustment.try_into() { - sctk_popup.data.positioner.set_anchor(positioner.anchor); - sctk_popup.data.positioner.set_anchor_rect( - positioner.anchor_rect.x, - positioner.anchor_rect.y, - positioner.anchor_rect.width, - positioner.anchor_rect.height, - ); - if let Ok(constraint_adjustment) = - positioner.constraint_adjustment.try_into() - { - sctk_popup.data.positioner.set_constraint_adjustment(constraint_adjustment); - } - sctk_popup.data.positioner.set_gravity(positioner.gravity); - sctk_popup.data.positioner.set_offset( - positioner.offset.0, - positioner.offset.1, - ); - if positioner.reactive { - sctk_popup.data.positioner.set_reactive(); - } - let guard =sctk_popup.common.lock().unwrap(); - let w = guard.size.width; - let h = guard.size.height; - drop(guard); - let size = positioner.size.unwrap_or((w, h)); - sctk_popup.popup - .xdg_surface() - .set_window_geometry(0, 0, w as i32, h as i32); - sctk_popup.update_viewport(w, h); - // update positioner - sctk_popup.data.positioner.set_size(w as i32, h as i32); - sctk_popup.popup.reposition(&sctk_popup.data.positioner, TOKEN_CTR.fetch_add(1, std::sync::atomic::Ordering::Relaxed)); let surface = sctk_popup.popup.wl_surface().clone(); - _ = send_event(&self.events_sender, &self.proxy, - SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Size(size.0, size.1), toplevel_id: sctk_popup.data.parent.wl_surface().clone(), parent_id: sctk_popup.data.parent.wl_surface().clone(), id: surface, parent_window: sctk_popup.data.parent_window }); + sctk_popup.data.positioner.set_constraint_adjustment(constraint_adjustment); } - }, - } + sctk_popup.data.positioner.set_gravity(positioner.gravity); + sctk_popup.data.positioner.set_offset( + positioner.offset.0, + positioner.offset.1, + ); + if positioner.reactive { + sctk_popup.data.positioner.set_reactive(); + } + let guard =sctk_popup.common.lock().unwrap(); + let w = guard.size.width; + let h = guard.size.height; + drop(guard); + let size = positioner.size.unwrap_or((w, h)); + sctk_popup.popup + .xdg_surface() + .set_window_geometry(0, 0, w as i32, h as i32); + sctk_popup.update_viewport(w, h); + // update positioner + sctk_popup.data.positioner.set_size(w as i32, h as i32); + sctk_popup.popup.reposition(&sctk_popup.data.positioner, TOKEN_CTR.fetch_add(1, std::sync::atomic::Ordering::Relaxed)); let surface = sctk_popup.popup.wl_surface().clone(); + _ = send_event(&self.events_sender, &self.proxy, + SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Size(size.0, size.1), toplevel_id: sctk_popup.data.parent.wl_surface().clone(), parent_id: sctk_popup.data.parent.wl_surface().clone(), id: surface }); + } + }, }, Action::Activation(activation_event) => match activation_event { platform_specific::wayland::activation::Action::RequestToken { app_id, window, channel } => { @@ -1671,7 +1718,7 @@ impl SctkState { } let s = if let Some(w) = self.windows.iter_mut().find(|w| w.id == id) { Some((Surface::Xdg(w.xdg_surface(&self.connection), Some(w.xdg_toplevel(&self.connection))), w.window.surface_size().cast::().to_logical(w.window.scale_factor()))) - } else if let Some(p) = self.popmgr.popup_id_mut(id) { + } else if let Some(p) = self.popups.iter_mut().find(|w| w.data.id == id) { let guard = p.common.lock().unwrap(); Some((Surface::Xdg(p.popup.xdg_surface().clone(), None), guard.size.cast::())) } else if let Some(l) = self.layer_surfaces.iter_mut().find(|l| l.id == id) { @@ -1775,7 +1822,7 @@ impl SctkState { return Ok(()); } - let s = if let Some(s) = self.popmgr.popup_id(id) { + let s = if let Some(s) = self.popups.iter().find(|s| s.data.id == id) { s.popup.wl_surface() } else if let Some(s) = self.layer_surfaces.iter().find(|s| s.id == id) { s.surface.wl_surface() @@ -1796,76 +1843,6 @@ impl SctkState { Ok(()) } - fn destroy_popup( - &mut self, - id: core::window::Id, - ignore_children: bool, - ) -> Option> { - let Some(to_destroy) = self.popmgr.popup_id(id) else { - if self.pending_popup.take().is_none_or(|p_id| p_id.0.id != id) { - log::warn!( - "Destroyed popup does not exist. {id:?}, existing: {:?}", - self.popmgr.popups().map(|p| p.data.id).collect::>() - ); - } - return Some(Ok(())); - }; - let s = to_destroy.popup.wl_surface().clone(); - let d: Option> = if ignore_children { - self.popmgr.remove(&s).map(|v| v.collect()) - } else { - self.popmgr.remove_ignore_children(&s).map(|v| v.collect()) - }; - let Some(to_destroy) = d else { - return Some(Ok(())); - }; - - for popup in to_destroy { - if let Some(id) = self.id_map.remove(&popup.popup.wl_surface().id()) - { - _ = self.destroyed.insert(id); - } - - if let Some(blurred) = self.blur_surfaces.remove(&id) { - blurred.destroy(); - } - _ = self.corner_radii.remove(&id); - - let (removed, remaining): (Vec<_>, Vec<_>) = self - .subsurfaces - .drain(..) - .partition(|s| s.instance.parent == *popup.popup.wl_surface()); - - self.subsurfaces = remaining; - for s in removed { - crate::subsurface_widget::remove_iced_subsurface( - &s.instance.wl_surface, - ); - send_event( - &self.events_sender, - &self.proxy, - SctkEvent::SubsurfaceEvent( - crate::sctk_event::SubsurfaceEventVariant::Destroyed( - s.instance, - ), - ), - ); - } - _ = send_event( - &self.events_sender, - &self.proxy, - SctkEvent::PopupEvent { - variant: crate::sctk_event::PopupEventVariant::Done, - parent_window: popup.data.parent_window, - toplevel_id: popup.data.toplevel.clone(), - parent_id: popup.data.parent.wl_surface().clone(), - id: popup.popup.wl_surface().clone(), - }, - ); - } - None - } - fn apply_blur( &mut self, id: core::window::Id, @@ -1945,7 +1922,7 @@ impl SctkState { }; if self.layer_surfaces.iter().any(|s| s.id == settings.id) || self.windows.iter().any(|w| w.id == settings.id) - || self.popmgr.popup_id(settings.id).is_some() + || self.popups.iter().any(|p| p.data.id == settings.id) || self.subsurfaces.iter().any(|s| s.id == settings.id) { log::warn!("Subsurface with id {:?} already exists", settings.id); @@ -2000,7 +1977,12 @@ impl SctkState { self.windows.iter().find(|w| w.id == settings.parent) { PopupParent::Window(parent.wl_surface(&self.connection)) - } else if let Some(parent) = self.popmgr.popup_id(settings.parent) { + } else if let Some(i) = self + .popups + .iter() + .position(|p| p.data.id == settings.parent) + { + let parent = &self.popups[i]; PopupParent::Popup(parent.popup.wl_surface().clone()) } else if let Some(i) = self .lock_surfaces diff --git a/winit/src/platform_specific/wayland/handlers/seat/pointer.rs b/winit/src/platform_specific/wayland/handlers/seat/pointer.rs index ac0bd4a9..b40912ec 100644 --- a/winit/src/platform_specific/wayland/handlers/seat/pointer.rs +++ b/winit/src/platform_specific/wayland/handlers/seat/pointer.rs @@ -31,7 +31,7 @@ impl PointerHandler for SctkState { // track events, but only forward for the active seat for e in events { - if !my_seat.hidden && my_seat.active_icon != my_seat.icon { + if my_seat.active_icon != my_seat.icon { // Restore cursor that was set by appliction, or default my_seat.set_cursor( conn, @@ -60,19 +60,10 @@ impl PointerHandler for SctkState { match e.kind { PointerEventKind::Enter { .. } => { _ = my_seat.ptr_focus.replace(e.surface.clone()); - if let Some(want) = - self.cursor_requests.get(&e.surface.id()) - { - my_seat.icon = want.icon; - my_seat.set_cursor_visible(conn, !want.hidden); - } } PointerEventKind::Leave { .. } => { _ = my_seat.ptr_focus.take(); _ = my_seat.active_icon = None; - // The next surface the pointer lands on decides what it - // looks like there - my_seat.hidden = false; } PointerEventKind::Press { time, diff --git a/winit/src/platform_specific/wayland/handlers/seat/seat.rs b/winit/src/platform_specific/wayland/handlers/seat/seat.rs index ec2b187c..ee6b0e6e 100644 --- a/winit/src/platform_specific/wayland/handlers/seat/seat.rs +++ b/winit/src/platform_specific/wayland/handlers/seat/seat.rs @@ -38,7 +38,6 @@ impl SeatHandler for SctkState { last_touch_down: None, icon: None, active_icon: None, - hidden: false, }); } @@ -66,7 +65,6 @@ impl SeatHandler for SctkState { last_touch_down: None, icon: None, active_icon: None, - hidden: false, }); self.seats.last_mut().unwrap() } diff --git a/winit/src/platform_specific/wayland/handlers/shell/xdg_popup.rs b/winit/src/platform_specific/wayland/handlers/shell/xdg_popup.rs index 4d11aac8..432f5bde 100644 --- a/winit/src/platform_specific/wayland/handlers/shell/xdg_popup.rs +++ b/winit/src/platform_specific/wayland/handlers/shell/xdg_popup.rs @@ -1,6 +1,6 @@ use crate::event_loop::state::receive_frame; use crate::platform_specific::wayland::{ - event_loop::state::{PopupParent, SctkState}, + event_loop::state::{self, PopupParent, SctkState}, sctk_event::{PopupEventVariant, SctkEvent}, }; use cctk::sctk::{ @@ -18,11 +18,11 @@ impl PopupHandler for SctkState { configure: cctk::sctk::shell::xdg::popup::PopupConfigure, ) { self.request_redraw(popup.wl_surface()); - let sctk_popup = match self.popmgr.popup_mut(popup.wl_surface()) { + let sctk_popup = match self.popups.iter_mut().find(|s| { + s.popup.wl_surface().clone() == popup.wl_surface().clone() + }) { Some(p) => p, - None => { - return; - } + None => return, }; let first = sctk_popup.last_configure.is_none(); _ = sctk_popup.last_configure.replace(configure.clone()); @@ -38,7 +38,6 @@ impl PopupHandler for SctkState { first, ), id: popup.wl_surface().clone(), - parent_window: sctk_popup.data.parent_window, toplevel_id: sctk_popup.data.toplevel.clone(), parent_id: match &sctk_popup.data.parent { PopupParent::LayerSurface(s) => s.clone(), @@ -54,11 +53,35 @@ impl PopupHandler for SctkState { _qh: &cctk::sctk::reexports::client::QueueHandle, popup: &cctk::sctk::shell::xdg::popup::Popup, ) { - let Some(to_destroy) = self.popmgr.remove(popup.wl_surface()) else { - return; + let sctk_popup = match self.popups.iter().position(|s| { + s.popup.wl_surface().clone() == popup.wl_surface().clone() + }) { + Some(p) => self.popups.remove(p), + None => return, }; - - for popup in to_destroy { + let mut to_destroy = vec![sctk_popup]; + while let Some(popup_to_destroy) = to_destroy.last() { + match popup_to_destroy.data.parent.clone() { + state::PopupParent::LayerSurface(_) + | state::PopupParent::Window(_) => { + break; + } + state::PopupParent::Popup(popup_to_destroy_first) => { + let Some(popup_to_destroy_first) = + self.popups.iter().position(|p| { + p.popup.wl_surface() == &popup_to_destroy_first + }) + else { + log::warn!("could not find popup to destroy first."); + return; + }; + let popup_to_destroy_first = + self.popups.remove(popup_to_destroy_first); + to_destroy.push(popup_to_destroy_first); + } + } + } + for popup in to_destroy.into_iter().rev() { if let Some(id) = self.id_map.remove(&popup.popup.wl_surface().id()) { if let Some(blurred) = self.blur_surfaces.remove(&id) { @@ -71,7 +94,6 @@ impl PopupHandler for SctkState { self.sctk_events.push(SctkEvent::PopupEvent { variant: PopupEventVariant::Done, - parent_window: popup.data.parent_window, toplevel_id: popup.data.toplevel.clone(), parent_id: popup.data.parent.wl_surface().clone(), id: popup.popup.wl_surface().clone(), diff --git a/winit/src/platform_specific/wayland/mod.rs b/winit/src/platform_specific/wayland/mod.rs index 289d410d..e98316ce 100644 --- a/winit/src/platform_specific/wayland/mod.rs +++ b/winit/src/platform_specific/wayland/mod.rs @@ -32,8 +32,7 @@ use winit::window::ImePurpose; pub(crate) enum Action { Action(iced_runtime::platform_specific::wayland::Action), - SetCursor(ObjectId, CursorIcon), - SetCursorVisible(ObjectId, bool), + SetCursor(CursorIcon), RequestRedraw(ObjectId), TrackWindow(Arc, window::Id), ResizeWindow(window::Id), @@ -49,14 +48,9 @@ impl std::fmt::Debug for Action { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Action(arg0) => f.debug_tuple("Action").field(arg0).finish(), - Self::SetCursor(arg0, arg1) => { - f.debug_tuple("SetCursor").field(arg0).field(arg1).finish() + Self::SetCursor(arg0) => { + f.debug_tuple("SetCursor").field(arg0).finish() } - Self::SetCursorVisible(arg0, arg1) => f - .debug_tuple("SetCursorVisible") - .field(arg0) - .field(arg1) - .finish(), Self::RequestRedraw(arg0) => { f.debug_tuple("RequestRedraw").field(arg0).finish() } diff --git a/winit/src/platform_specific/wayland/sctk_event.rs b/winit/src/platform_specific/wayland/sctk_event.rs index 64609b58..2ac54bed 100755 --- a/winit/src/platform_specific/wayland/sctk_event.rs +++ b/winit/src/platform_specific/wayland/sctk_event.rs @@ -155,9 +155,6 @@ pub enum SctkEvent { parent_id: WlSurface, /// the id of this popup id: WlSurface, - /// Window id of the parent surface, as passed in the popup settings. Needed to route - /// `Done` to the parent's widgets. - parent_window: SurfaceId, }, SubsurfaceEvent(SubsurfaceEventVariant), @@ -923,7 +920,6 @@ impl SctkEvent { SctkEvent::PopupEvent { variant, id: surface, - parent_window, .. } => { match variant { @@ -962,8 +958,6 @@ impl SctkEvent { ) }) { - // tell the parent's widgets so menus can reset when the compositor dismisses them. - events.push((Some(parent_window), e.1.clone())); events.push(e) } } diff --git a/winit/src/platform_specific/wayland/winit_window.rs b/winit/src/platform_specific/wayland/winit_window.rs index 2751b31c..b33b7f9e 100644 --- a/winit/src/platform_specific/wayland/winit_window.rs +++ b/winit/src/platform_specific/wayland/winit_window.rs @@ -83,10 +83,7 @@ impl winit::window::Window for SctkWinitWindow { fn set_cursor(&self, cursor: winit_core::cursor::Cursor) { match cursor { winit_core::cursor::Cursor::Icon(icon) => { - _ = self.tx.send(Action::SetCursor( - self.surface.wl_surface().id(), - icon, - )); + _ = self.tx.send(Action::SetCursor(icon)); } winit_core::cursor::Cursor::Custom(_) => { // TODO @@ -95,10 +92,7 @@ impl winit::window::Window for SctkWinitWindow { } fn set_cursor_visible(&self, visible: bool) { - _ = self.tx.send(Action::SetCursorVisible( - self.surface.wl_surface().id(), - visible, - )); + // TODO } fn surface_size(&self) -> winit::dpi::PhysicalSize { diff --git a/winit/src/window.rs b/winit/src/window.rs index 18f5f41f..e56483f4 100644 --- a/winit/src/window.rs +++ b/winit/src/window.rs @@ -291,11 +291,11 @@ where pub fn update_mouse(&mut self, interaction: mouse::Interaction) { if interaction != self.mouse_interaction { if let Some(icon) = conversion::mouse_interaction(interaction) { + self.raw.set_cursor(icon.into()); + if self.mouse_interaction == mouse::Interaction::Hidden { self.raw.set_cursor_visible(true); } - - self.raw.set_cursor(icon.into()); } else { self.raw.set_cursor_visible(false); }