merge: pop-os/iced a l'etat pinne par libcosmic 08a09e3df (epoch-1.9.0)
Some checks failed
Audit / vulnerabilities (push) Has been cancelled
Check / wasm (push) Has been cancelled
Check / widget (push) Has been cancelled
Document / all (push) Has been cancelled
Format / all (push) Has been cancelled
Lint / all (push) Has been cancelled
Test / all (macOS-latest, 1.88) (push) Has been cancelled
Test / all (macOS-latest, beta) (push) Has been cancelled
Test / all (macOS-latest, stable) (push) Has been cancelled
Test / all (ubuntu-latest, 1.88) (push) Has been cancelled
Test / all (ubuntu-latest, beta) (push) Has been cancelled
Test / all (ubuntu-latest, stable) (push) Has been cancelled
Test / all (windows-latest, 1.88) (push) Has been cancelled
Test / all (windows-latest, beta) (push) Has been cancelled
Test / all (windows-latest, stable) (push) Has been cancelled
Some checks failed
Audit / vulnerabilities (push) Has been cancelled
Check / wasm (push) Has been cancelled
Check / widget (push) Has been cancelled
Document / all (push) Has been cancelled
Format / all (push) Has been cancelled
Lint / all (push) Has been cancelled
Test / all (macOS-latest, 1.88) (push) Has been cancelled
Test / all (macOS-latest, beta) (push) Has been cancelled
Test / all (macOS-latest, stable) (push) Has been cancelled
Test / all (ubuntu-latest, 1.88) (push) Has been cancelled
Test / all (ubuntu-latest, beta) (push) Has been cancelled
Test / all (ubuntu-latest, stable) (push) Has been cancelled
Test / all (windows-latest, 1.88) (push) Has been cancelled
Test / all (windows-latest, beta) (push) Has been cancelled
Test / all (windows-latest, stable) (push) Has been cancelled
# Conflicts: # Cargo.toml
This commit is contained in:
commit
24da1dc0e4
23 changed files with 1602 additions and 1231 deletions
|
|
@ -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<F: Fn(window::Id) -> bool>(
|
||||
&mut self,
|
||||
keep: F,
|
||||
|
|
|
|||
|
|
@ -138,3 +138,14 @@ pub fn set_input_zone<Message>(
|
|||
)),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn set_show_on_lock<Message>(
|
||||
id: SurfaceId,
|
||||
value: bool,
|
||||
) -> Task<Message> {
|
||||
task::effect(Action::PlatformSpecific(
|
||||
platform_specific::Action::Wayland(wayland::Action::LayerSurface(
|
||||
wayland::layer_surface::Action::ShowOnLock { id, value },
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -392,6 +392,7 @@ impl SctkEventLoop {
|
|||
).ok(),
|
||||
text_input_manager: TextInputManager::try_new(®istry_state, &qh),
|
||||
ext_background_effect_manager: ext_background_effect::ExtBackgroundEffectManager::new(&globals, &qh).ok(),
|
||||
cosmic_session_lock_layer_manager: registry_state.bind_one(&qh, 1..=1, ()).ok(),
|
||||
registry_state,
|
||||
|
||||
queue_handle: qh,
|
||||
|
|
@ -411,6 +412,7 @@ impl SctkEventLoop {
|
|||
lock_surfaces: Vec::new(),
|
||||
subsurfaces: Vec::new(),
|
||||
touch_points: HashMap::new(),
|
||||
kbd_leave_to_own_popup: HashSet::new(),
|
||||
sctk_events: Vec::new(),
|
||||
frame_status: HashMap::new(),
|
||||
cursor_requests: HashMap::new(),
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ use cctk::{
|
|||
cosmic_protocols::{
|
||||
corner_radius::v1::client::cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1,
|
||||
overlap_notify::v1::client::zcosmic_overlap_notification_v1::ZcosmicOverlapNotificationV1,
|
||||
session_lock_layer::v1::client::cosmic_session_lock_layer_manager_v1::CosmicSessionLockLayerManagerV1,
|
||||
},
|
||||
sctk::{
|
||||
activation::{ActivationState, RequestData},
|
||||
|
|
@ -434,6 +435,9 @@ pub struct SctkState {
|
|||
pub(crate) blur_surfaces: HashMap<core::window::Id, ExtBackgroundEffectSurfaceV1>,
|
||||
pub(crate) corner_radii: HashMap<core::window::Id, (SctkCornerRadius, Option<CornerRadius>)>,
|
||||
pub(crate) touch_points: HashMap<touch::Finger, (WlSurface, Point)>,
|
||||
/// List of layer surfaces whose keyboard leave was ignored because focus moved
|
||||
/// to one of their own grabbing popups. Their next enter is swallowed too.
|
||||
pub(crate) kbd_leave_to_own_popup: HashSet<ObjectId>,
|
||||
|
||||
/// Window updates, which are coming from SCTK or the compositor, which require
|
||||
/// calling back to the sctk's downstream. They are handled right in the event loop,
|
||||
|
|
@ -477,6 +481,7 @@ pub struct SctkState {
|
|||
pub(crate) toplevel_manager: Option<ToplevelManagerState>,
|
||||
pub(crate) subsurface_state: Option<SubsurfaceState>,
|
||||
pub(crate) ext_background_effect_manager: Option<ext_background_effect::ExtBackgroundEffectManager>,
|
||||
pub(crate) cosmic_session_lock_layer_manager: Option<CosmicSessionLockLayerManagerV1>,
|
||||
|
||||
pub(crate) activation_token_ctr: u32,
|
||||
pub(crate) token_senders: HashMap<u32, oneshot::Sender<Option<String>>>,
|
||||
|
|
@ -679,6 +684,33 @@ impl SctkState {
|
|||
// TODO winit sets cursor size after handling the change for the window, so maybe that should be done as well.
|
||||
}
|
||||
|
||||
/// Popups can only be attached to xdg or layer surfaces. So for subsurfaces, we return the
|
||||
/// parent, and adjust the offset.
|
||||
pub(crate) fn subsurface_popup_parent(
|
||||
&self,
|
||||
id: core::window::Id,
|
||||
) -> Option<(WlSurface, (i32, i32))> {
|
||||
let mut sub = self.subsurfaces.iter().find(|s| s.id == id)?;
|
||||
let mut offset = (0, 0);
|
||||
// bounded by the number of subsurfaces so a corrupt chain cannot spin forever
|
||||
for _ in 0..=self.subsurfaces.len() {
|
||||
if let Some(b) = sub.instance.bounds {
|
||||
offset.0 += b.x as i32;
|
||||
offset.1 += b.y as i32;
|
||||
}
|
||||
let parent = &sub.instance.parent;
|
||||
match self
|
||||
.subsurfaces
|
||||
.iter()
|
||||
.find(|s| s.instance.wl_surface == *parent)
|
||||
{
|
||||
Some(s) => sub = s,
|
||||
None => return Some((parent.clone(), offset)),
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_popup(
|
||||
&mut self,
|
||||
settings: SctkPopupSettings,
|
||||
|
|
@ -723,6 +755,27 @@ impl SctkState {
|
|||
PopupParent::Popup(parent.popup.wl_surface().clone()),
|
||||
parent.data.toplevel.clone(),
|
||||
)
|
||||
} else if let Some((surface, _)) =
|
||||
self.subsurface_popup_parent(settings.parent)
|
||||
{
|
||||
if self
|
||||
.layer_surfaces
|
||||
.iter()
|
||||
.any(|l| *l.surface.wl_surface() == surface)
|
||||
{
|
||||
(PopupParent::LayerSurface(surface.clone()), surface)
|
||||
} else if self
|
||||
.windows
|
||||
.iter()
|
||||
.any(|w| w.wl_surface(&self.connection) == surface)
|
||||
{
|
||||
(PopupParent::Window(surface.clone()), surface)
|
||||
} else if let Some(p) = self.popmgr.popup(&surface) {
|
||||
(PopupParent::Popup(surface), p.data.toplevel.clone())
|
||||
} else {
|
||||
// e.g. a session lock surface, which cannot parent xdg popups
|
||||
return Err(PopupCreationError::ParentMissing);
|
||||
}
|
||||
} else {
|
||||
return Err(PopupCreationError::ParentMissing);
|
||||
};
|
||||
|
|
@ -1156,6 +1209,7 @@ impl SctkState {
|
|||
}
|
||||
if let Some(i) = self.layer_surfaces.iter().position(|l| l.id == id) {
|
||||
let l = self.layer_surfaces.remove(i);
|
||||
_ = self.kbd_leave_to_own_popup.remove(&l.surface.wl_surface().id());
|
||||
|
||||
if let Some(blurred) = self.blur_surfaces.remove(&l.id) {
|
||||
blurred.destroy();
|
||||
|
|
@ -1266,16 +1320,36 @@ impl SctkState {
|
|||
}
|
||||
}
|
||||
},
|
||||
platform_specific::wayland::layer_surface::Action::ShowOnLock { id, value } => {
|
||||
if let Some(layer_surface) = self.layer_surfaces.iter_mut().find(|l| l.id == id) {
|
||||
if let SurfaceKind::Wlr(wlr) = layer_surface.surface.kind() {
|
||||
if let Some(manager) = &self.cosmic_session_lock_layer_manager {
|
||||
if value {
|
||||
manager.set_show_on_lock(wlr);
|
||||
} else {
|
||||
manager.unset_show_on_lock(wlr);
|
||||
}
|
||||
_ = self.to_commit.insert(id, layer_surface.surface.wl_surface().clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Action::Popup(action) => {
|
||||
match action {
|
||||
platform_specific::wayland::popup::Action::Popup { popup: settings } => {
|
||||
platform_specific::wayland::popup::Action::Popup { popup: mut settings } => {
|
||||
// anchor rects from widgets inside a subsurface are in that subsurface's coordinates
|
||||
if let Some((_, (dx, dy))) = self.subsurface_popup_parent(settings.parent) {
|
||||
settings.positioner.anchor_rect.x += dx;
|
||||
settings.positioner.anchor_rect.y += dy;
|
||||
}
|
||||
// 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(||
|
||||
|| self.layer_surfaces.iter().any(|l| l.id == settings.parent && p.data.parent.wl_surface() == l.surface.wl_surface())
|
||||
|| (p.data.parent_window == settings.parent && self.subsurfaces.iter().any(|s| s.id == settings.parent))).then(||
|
||||
{
|
||||
p.popup.wl_surface().clone()
|
||||
})).and_then(|s| self.popmgr.chain_for_popup_mut(&s))
|
||||
|
|
@ -1646,9 +1720,13 @@ impl SctkState {
|
|||
}
|
||||
},
|
||||
subsurface::Action::Reposition { id, x, y } => {
|
||||
if let Some(subsurface) = self.subsurfaces.iter().find(|s| s.id == id) {
|
||||
if let Some(subsurface) = self.subsurfaces.iter_mut().find(|s| s.id == id) {
|
||||
subsurface.instance.wl_subsurface.set_position(x, y);
|
||||
subsurface.instance.wl_surface.commit();
|
||||
if let Some(b) = subsurface.instance.bounds.as_mut() {
|
||||
b.x = x as f32;
|
||||
b.y = y as f32;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -2165,3 +2243,4 @@ pub(crate) fn send_event(
|
|||
|
||||
delegate_noop!(SctkState: ignore WlSubsurface);
|
||||
delegate_noop!(SctkState: ignore WlRegion);
|
||||
delegate_noop!(SctkState: CosmicSessionLockLayerManagerV1);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
use crate::platform_specific::wayland::{
|
||||
event_loop::state::SctkState,
|
||||
event_loop::state::{PopupParent, SctkState},
|
||||
sctk_event::{KeyboardEventVariant, SctkEvent},
|
||||
};
|
||||
use cctk::sctk::{
|
||||
delegate_keyboard,
|
||||
seat::keyboard::{KeyboardHandler, Keysym, Modifiers},
|
||||
};
|
||||
use cctk::sctk::{reexports::client::Proxy, seat::keyboard::RawModifiers};
|
||||
use cctk::sctk::{
|
||||
reexports::client::Proxy,
|
||||
seat::keyboard::RawModifiers,
|
||||
shell::{WaylandSurface, wlr_layer::KeyboardInteractivity},
|
||||
};
|
||||
|
||||
fn modifiers_from_keysyms(
|
||||
keysyms: &[Keysym],
|
||||
|
|
@ -90,6 +94,11 @@ impl KeyboardHandler for SctkState {
|
|||
}
|
||||
self.request_redraw(&surface);
|
||||
|
||||
// Focus is coming back from our own grabbing popup
|
||||
if self.kbd_leave_to_own_popup.remove(&surface.id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let surfaces = self.subsurfaces.iter().filter_map(|s| {
|
||||
(s.instance.parent == *surface).then(|| &s.instance.wl_surface)
|
||||
});
|
||||
|
|
@ -147,6 +156,32 @@ impl KeyboardHandler for SctkState {
|
|||
_ = my_seat.kbd_focus.take();
|
||||
(is_active, seat, kbd)
|
||||
};
|
||||
|
||||
// A grabbing popup parented to this layer surface, or opened from one of
|
||||
// its subsurfaces that holds focus, takes keyboard focus.
|
||||
// Only swallow the leave when the layer surface has exclusive keyboard interactivity.
|
||||
let focus_moved_to_own_popup = self.popmgr.popups().any(|p| {
|
||||
p.data.grab
|
||||
&& match &p.data.parent {
|
||||
PopupParent::LayerSurface(s) => {
|
||||
(s == surface
|
||||
|| self.subsurfaces.iter().any(|sub| {
|
||||
sub.id == p.data.parent_window
|
||||
&& sub.instance.wl_surface == *surface
|
||||
}))
|
||||
&& self.layer_surfaces.iter().any(|l| {
|
||||
l.surface.wl_surface() == s
|
||||
&& l.keyboard_interactivity
|
||||
== KeyboardInteractivity::Exclusive
|
||||
})
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
if focus_moved_to_own_popup {
|
||||
_ = self.kbd_leave_to_own_popup.insert(surface.id());
|
||||
return;
|
||||
}
|
||||
let surfaces = self.subsurfaces.iter().filter_map(|s| {
|
||||
(s.instance.parent == *surface).then(|| &s.instance.wl_surface)
|
||||
});
|
||||
|
|
@ -318,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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ObjectId, SurfaceIdWrapper>,
|
||||
subsurface_state: Option<SubsurfaceState>,
|
||||
surface_subsurfaces: HashMap<window::Id, Vec<SubsurfaceInstance>>,
|
||||
popup_toplevels: HashMap<ObjectId, window::Id>,
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
|
|
@ -623,7 +627,12 @@ impl SctkEvent {
|
|||
keyboard::Event::KeyPressed {
|
||||
key: key.clone(),
|
||||
location: location,
|
||||
text: ke.utf8.map(|s| s.into()),
|
||||
// sctk reports keys without a text representation (e.g. arrows, Home) as
|
||||
// Some("") but winit reports None. Widgets are written against winit's contract.
|
||||
text: ke
|
||||
.utf8
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|s| s.into()),
|
||||
modifiers: modifiers_to_native(*modifiers),
|
||||
physical_key,
|
||||
repeat: false,
|
||||
|
|
@ -649,7 +658,9 @@ impl SctkEvent {
|
|||
keyboard::Event::KeyPressed {
|
||||
key: key.clone(),
|
||||
location: location,
|
||||
text: utf8.map(|s| s.into()),
|
||||
text: utf8
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|s| s.into()),
|
||||
modifiers: modifiers_to_native(*modifiers),
|
||||
physical_key,
|
||||
repeat: true,
|
||||
|
|
@ -1400,6 +1411,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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue