diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 9a7a456a..db1192fe 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -1328,6 +1328,17 @@ async fn run_instance

( continue; } } + // to avoid race with the reported modifier state from the popup of + // this window, don't empty the modifiers, on focus loss + #[cfg(wayland_platform)] + if let winit::event::WindowEvent::ModifiersChanged(modifiers) = + &event + && modifiers.state().is_empty() + && platform_specific_handler.has_popup(id) + { + continue; + } + match event { winit::event::WindowEvent::SurfaceResized(_) => { window.raw.request_redraw(); diff --git a/winit/src/platform_specific/mod.rs b/winit/src/platform_specific/mod.rs index 969c8299..f1cbf2e2 100644 --- a/winit/src/platform_specific/mod.rs +++ b/winit/src/platform_specific/mod.rs @@ -74,6 +74,11 @@ impl PlatformSpecific { } } + #[cfg(wayland_platform)] + pub(crate) fn has_popup(&self, toplevel: window::Id) -> bool { + self.wayland.has_popup(toplevel) + } + pub(crate) fn retain_subsurfaces bool>( &mut self, keep: F, diff --git a/winit/src/platform_specific/wayland/handlers/seat/keyboard.rs b/winit/src/platform_specific/wayland/handlers/seat/keyboard.rs index 30f9c7a6..e3ad792a 100644 --- a/winit/src/platform_specific/wayland/handlers/seat/keyboard.rs +++ b/winit/src/platform_specific/wayland/handlers/seat/keyboard.rs @@ -353,6 +353,31 @@ impl KeyboardHandler for SctkState { surface: surface.clone(), }); } + // A popup holds the seat's keyboard on behalf of its toplevel. Report + // the modifiers to the toplevel as well. + let toplevel = self.popmgr.popups().find_map(|p| { + (p.popup.wl_surface() == &surface) + .then(|| p.data.toplevel.clone()) + }); + if let Some(toplevel) = toplevel { + let id = self + .windows + .iter() + .find(|w| w.wl_surface(&self.connection) == toplevel) + .map(|w| w.id) + .or_else(|| { + self.layer_surfaces + .iter() + .find(|l| *l.surface.wl_surface() == toplevel) + .map(|l| l.id) + }); + if let Some(toplevel) = id { + self.sctk_events.push(SctkEvent::PopupModifiers { + toplevel, + modifiers, + }); + } + } } } } diff --git a/winit/src/platform_specific/wayland/mod.rs b/winit/src/platform_specific/wayland/mod.rs index 289d410d..5b41e1c5 100644 --- a/winit/src/platform_specific/wayland/mod.rs +++ b/winit/src/platform_specific/wayland/mod.rs @@ -20,7 +20,7 @@ use iced_graphics::{Compositor, compositor}; use iced_runtime::core::{Vector, window}; use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle}; use raw_window_handle::{HasRawDisplayHandle, RawWindowHandle}; -use sctk_event::SctkEvent; +use sctk_event::{PopupEventVariant, SctkEvent}; use std::sync::OnceLock; use std::{collections::HashMap, sync::Arc}; use subsurface_widget::{SubsurfaceInstance, SubsurfaceState}; @@ -103,6 +103,7 @@ pub(crate) struct WaylandSpecific { surface_ids: HashMap, subsurface_state: Option, surface_subsurfaces: HashMap>, + popup_toplevels: HashMap, } impl PlatformSpecific { @@ -173,6 +174,11 @@ impl WaylandSpecific { self.conn.as_ref() } + /// Whether a popup opened from `toplevel` is alive. + pub(crate) fn has_popup(&self, toplevel: window::Id) -> bool { + self.popup_toplevels.values().any(|id| *id == toplevel) + } + pub(crate) async fn handle_event<'a, 'b, P>( &mut self, e: SctkEvent, @@ -201,8 +207,33 @@ impl WaylandSpecific { modifiers, subsurface_state, surface_subsurfaces, + popup_toplevels, } = self; + if let SctkEvent::PopupEvent { + variant, + parent_id, + parent_window, + id, + .. + } = &e + { + match variant { + PopupEventVariant::Created(..) => { + // A nested popup's parent is itself a popup so inhreit its toplevel + let toplevel = popup_toplevels + .get(&parent_id.id()) + .copied() + .unwrap_or(*parent_window); + _ = popup_toplevels.insert(id.id(), toplevel); + } + PopupEventVariant::Done => { + _ = popup_toplevels.remove(&id.id()); + } + _ => {} + } + } + match e { sctk_event => { let Some(sender) = sender.as_ref() else { diff --git a/winit/src/platform_specific/wayland/sctk_event.rs b/winit/src/platform_specific/wayland/sctk_event.rs index 64609b58..8e771f60 100755 --- a/winit/src/platform_specific/wayland/sctk_event.rs +++ b/winit/src/platform_specific/wayland/sctk_event.rs @@ -198,6 +198,10 @@ pub enum SctkEvent { SessionUnlocked, SurfaceScaleFactorChanged(f64, WlSurface, window::Id), Winit(WindowId, WindowEvent), + PopupModifiers { + toplevel: window::Id, + modifiers: cctk::sctk::seat::keyboard::Modifiers, + }, Subcompositor(SubsurfaceState), ShortcutsInhibited(bool), } @@ -1400,6 +1404,19 @@ impl SctkEvent { ), )), SctkEvent::Winit(_, _) => {} + SctkEvent::PopupModifiers { + toplevel, + modifiers, + } => { + events.push(( + Some(toplevel), + iced_runtime::core::Event::Keyboard( + keyboard::Event::ModifiersChanged(modifiers_to_native( + modifiers, + )), + ), + )); + } SctkEvent::SurfaceScaleFactorChanged(scale, _, id) => { if let Some(w) = window_manager.get_mut(id) { w.state.update_scale_factor(scale);