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);