From 8c309d52310cd45c0aa5842f6d90280fe3b41b9c Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Tue, 26 May 2026 01:11:40 +0200 Subject: [PATCH 01/64] i18n: translation updates from weblate Co-authored-by: therealmate Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-comp/hu/ Translation: Pop OS/COSMIC Comp --- resources/i18n/hu/cosmic_comp.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/i18n/hu/cosmic_comp.ftl b/resources/i18n/hu/cosmic_comp.ftl index 28d11a17..8175592c 100644 --- a/resources/i18n/hu/cosmic_comp.ftl +++ b/resources/i18n/hu/cosmic_comp.ftl @@ -1,5 +1,5 @@ a11y-zoom-move-continuously = A nézet folyamatosan követi a mutatót -a11y-zoom-move-onedge = A nézet csak akkor mozog, ha a mutató a szélen van +a11y-zoom-move-onedge = A nézet csak akkor mozog, ha a mutató eléri a szélét a11y-zoom-move-centered = A nézet úgy mozog, hogy a mutató középen maradjon a11y-zoom-settings = Nagyító beállításai… grow-window = Növelés From 22fe41974710794e63897d917d97e7b4c1d39871 Mon Sep 17 00:00:00 2001 From: devpa Date: Wed, 15 Apr 2026 10:30:58 +0300 Subject: [PATCH 02/64] input: allow layout switching and brightness/volume control under session lock --- src/input/actions.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/input/actions.rs b/src/input/actions.rs index 975aea78..3d017697 100644 --- a/src/input/actions.rs +++ b/src/input/actions.rs @@ -52,6 +52,14 @@ impl State { action, Action::Shortcut(shortcuts::Action::Terminate) | Action::Shortcut(shortcuts::Action::Debug) + | Action::Shortcut(shortcuts::Action::System( + shortcuts::action::System::InputSourceSwitch + | shortcuts::action::System::BrightnessDown + | shortcuts::action::System::BrightnessUp + | shortcuts::action::System::VolumeLower + | shortcuts::action::System::VolumeRaise + | shortcuts::action::System::Mute, + )) ) { return; From 32b54cc31f87e93782308d43e15b4020f2fed38e Mon Sep 17 00:00:00 2001 From: Skygrango Date: Wed, 6 May 2026 00:26:00 +0800 Subject: [PATCH 03/64] shell: implement surface_offset and surface_tree_offset --- src/shell/element/mod.rs | 8 +++++ src/shell/element/surface.rs | 67 +++++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 4696aa2e..d5b79703 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -254,6 +254,14 @@ impl CosmicMapped { .any(|(w, _)| w.has_surface(surface, surface_type)) } + pub fn surface_offset(&self, surface: &WlSurface) -> Option> { + self.windows().find_map(|(window, window_offset)| { + window + .surface_offset(surface) + .map(|offset| window_offset + offset) + }) + } + /// Give the pointer target under a relative offset into this element. /// /// Returns Target + Offset relative to the target diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 1a1e214b..769ffbd9 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -45,7 +45,10 @@ use smithay::{ IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, user_data::UserDataMap, }, wayland::{ - compositor::{SurfaceData, TraversalAction, with_states, with_surface_tree_downward}, + compositor::{ + SubsurfaceCachedState, SurfaceData, TraversalAction, with_states, + with_surface_tree_downward, + }, seat::WaylandFocus, shell::xdg::{ SurfaceCachedState, ToplevelCachedState, ToplevelSurface, XdgToplevelSurfaceData, @@ -652,6 +655,68 @@ impl CosmicSurface { } } + pub fn surface_offset(&self, surface: &WlSurface) -> Option> { + match self.0.underlying_surface() { + WindowSurface::Wayland(toplevel) => { + Self::surface_tree_offset(toplevel.wl_surface(), surface) + } + WindowSurface::X11(surface_x11) => { + if surface_x11.wl_surface().as_ref() == Some(surface) { + Some(Point::default()) + } else { + None + } + } + } + } + + pub fn surface_tree_offset( + root: &WlSurface, + surface: &WlSurface, + ) -> Option> { + if root == surface { + return Some(Point::default()); + } + + let found = AtomicBool::new(false); + let mut found_offset = Point::::default(); + + with_surface_tree_downward( + root, + Point::::default(), + |s, states, parent_offset| { + let mut offset = *parent_offset; + if s != root { + offset += states + .cached_state + .get::() + .current() + .location; + } + TraversalAction::DoChildren(offset) + }, + |s, _, offset| { + if s == surface { + found_offset = *offset; + found.store(true, Ordering::SeqCst); + } + }, + |_, _, _| !found.load(Ordering::SeqCst), + ); + + if found.load(Ordering::SeqCst) { + return Some(found_offset); + } + + for (popup, popup_offset) in PopupManager::popups_for_surface(root) { + if let Some(offset) = Self::surface_tree_offset(popup.wl_surface(), surface) { + return Some(popup_offset + offset); + } + } + + None + } + pub fn focus_under( &self, relative_pos: Point, From d829ec3815ff8f767bea44f4e3e15ea02198e18e Mon Sep 17 00:00:00 2001 From: Skygrango Date: Fri, 8 May 2026 14:10:05 +0800 Subject: [PATCH 04/64] shell: implement has_surface for KeyboardFocusTarget --- src/shell/focus/target.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index b177f00f..ef749a64 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -281,6 +281,18 @@ impl KeyboardFocusTarget { false } } + + pub fn has_surface(&self, shell: &Shell, surface: &WlSurface) -> bool { + if let Some(fe) = shell.focused_element(self) { + fe.has_surface(surface, WindowSurfaceType::ALL) + } else if let KeyboardFocusTarget::Fullscreen(s) = self { + s.has_surface(surface, WindowSurfaceType::ALL) + } else if let Some(root) = WaylandFocus::wl_surface(self) { + CosmicSurface::surface_tree_offset(&root, surface).is_some() + } else { + false + } + } } #[derive(Debug, Clone)] From e75c9c0c483f1771faf549a9b4993344f078c477 Mon Sep 17 00:00:00 2001 From: Skygrango Date: Wed, 13 May 2026 19:46:29 +0800 Subject: [PATCH 05/64] shell: implement surface_geometry_offset_from_toplevel for WorkspaceSet --- src/shell/mod.rs | 82 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index e1a48567..061be6b4 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -50,10 +50,13 @@ use smithay::{ }, utils::{IsAlive, Logical, Point, Rectangle, Serial, Size}, wayland::{ - compositor::{SurfaceAttributes, with_states}, + compositor::{SurfaceAttributes, get_parent, with_states}, seat::WaylandFocus, session_lock::LockSurface, - shell::wlr_layer::{KeyboardInteractivity, Layer, LayerSurfaceCachedState}, + shell::{ + wlr_layer::{KeyboardInteractivity, Layer, LayerSurfaceCachedState}, + xdg::{XDG_POPUP_ROLE, XdgPopupSurfaceData}, + }, xdg_activation::XdgActivationState, xwayland_keyboard_grab::XWaylandKeyboardGrab, }, @@ -728,6 +731,81 @@ impl WorkspaceSet { self.post_remove_workspace(workspace_state, &previous_active_handle); prefers } + + pub fn surface_geometry_offset_from_toplevel( + &self, + surface: &WlSurface, + ) -> Option<(Rectangle, Point)> { + let mut root = surface.clone(); + loop { + while let Some(parent) = get_parent(&root) { + root = parent; + } + if smithay::wayland::compositor::get_role(&root) == Some(XDG_POPUP_ROLE) + && let Some(parent) = with_states(&root, |states| { + states + .data_map + .get::() + .and_then(|m| m.lock().unwrap().parent.as_ref().cloned()) + }) + { + root = parent; + continue; + } + break; + } + + self.sticky_layer + .mapped() + .find(|w| { + w.windows() + .any(|(w, _)| w.wl_surface().as_deref() == Some(&root)) + }) + .and_then(|w| { + w.surface_offset(surface).and_then(|offset| { + self.sticky_layer + .element_geometry(w) + .map(|geom| (geom, offset)) + }) + }) + .or_else(|| { + self.workspaces.iter().find_map(|workspace| { + workspace + .get_fullscreen() + .and_then(|fullscreen| { + (fullscreen.wl_surface().as_deref() == Some(&root)) + .then(|| { + fullscreen.surface_offset(surface).and_then(|offset| { + workspace.fullscreen_geometry().map(|geom| (geom, offset)) + }) + }) + .flatten() + }) + .or_else(|| { + workspace.mapped().find_map(|w| { + w.windows() + .any(|(w, _)| w.wl_surface().as_deref() == Some(&root)) + .then(|| { + w.surface_offset(surface).and_then(|offset| { + workspace.element_geometry(w).map(|geom| (geom, offset)) + }) + }) + .flatten() + }) + }) + }) + }) + .or_else(|| { + layer_map_for_output(&self.output).layers().find_map(|l| { + (l.wl_surface() == &root) + .then(|| { + CosmicSurface::surface_tree_offset(l.wl_surface(), surface) + .map(|offset| (l.geometry().as_local(), offset)) + }) + .flatten() + }) + }) + } } #[derive(Debug)] From a533ac1ec39915da87f25630369185135b6ac12e Mon Sep 17 00:00:00 2001 From: Skygrango Date: Wed, 13 May 2026 19:53:07 +0800 Subject: [PATCH 06/64] input: implement apply_cursor_hint --- src/input/mod.rs | 135 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 132 insertions(+), 3 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 548a60ab..4e1ba33e 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -52,16 +52,21 @@ use smithay::{ AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent, - PointerGrab, RelativeMotionEvent, + PointerGrab, PointerHandle, RelativeMotionEvent, }, touch::{DownEvent, MotionEvent as TouchMotionEvent, UpEvent}, }, output::Output, reexports::{ - input::Device as InputDevice, wayland_server::protocol::wl_shm::Format as ShmFormat, + input::Device as InputDevice, + wayland_server::{ + Resource as _, + protocol::{wl_shm::Format as ShmFormat, wl_surface::WlSurface}, + }, }, - utils::{Point, Rectangle, SERIAL_COUNTER, Serial}, + utils::{Logical, Point, Rectangle, SERIAL_COUNTER, Serial}, wayland::{ + compositor::CompositorHandler, image_copy_capture::{BufferConstraints, CursorSessionRef}, keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat, pointer_constraints::{PointerConstraint, with_pointer_constraint}, @@ -2317,6 +2322,130 @@ impl State { .ok() .flatten() } + + pub fn apply_cursor_hint( + &mut self, + surface: &WlSurface, + pointer: &PointerHandle, + mut location: Point, + ) { + if let Some(client) = surface.client() { + let scale = self.client_compositor_state(&client).client_scale(); + location.x /= scale; + location.y /= scale; + } + + let point_and_output = { + let shell = self.common.shell.read(); + let found = shell.workspaces.sets.iter().find_map(|(out, set)| { + set.surface_geometry_offset_from_toplevel(surface) + .map(|(geometry, surface_offset)| (out, geometry, surface_offset)) + }); + + if let Some((output, geometry, surface_offset)) = found { + let mut pos_in_element = location + surface_offset.to_f64(); + let window_size = geometry.size.to_f64(); + + let is_legal = |p: Point| { + let in_window = p.x >= 0.0 + && p.y >= 0.0 + // hack: prevent the cursor from touching the edge of the window + && p.x <= window_size.w - 1. + && p.y <= window_size.h - 1.; + if !in_window { + return false; + } + + with_pointer_constraint(surface, pointer, |constraint| { + if let Some(constraint) = constraint + && let Some(region) = constraint.region() + { + let point_in_surface = (p - surface_offset.to_f64()).to_i32_round(); + return region.contains(point_in_surface); + } + true + }) + }; + + let workspace_origin = output.geometry().loc.to_f64(); + let origin = geometry.loc.to_f64(); + + if !is_legal(pos_in_element) { + let original_global = pointer.current_location(); + + let original_pos_in_element = Point::new( + original_global.x - workspace_origin.x - origin.x, + original_global.y - workspace_origin.y - origin.y, + ); + + let y_only_pos = Point::new(original_pos_in_element.x, pos_in_element.y); + let x_only_pos = Point::new(pos_in_element.x, original_pos_in_element.y); + + if is_legal(y_only_pos) { + pos_in_element = y_only_pos; + } else if is_legal(x_only_pos) { + pos_in_element = x_only_pos; + } else { + pos_in_element = original_pos_in_element; + } + } + + let x = workspace_origin.x + origin.x + pos_in_element.x; + let y = workspace_origin.y + origin.y + pos_in_element.y; + Some((Point::new(x, y), output.clone())) + } else { + None + } + }; + + if let Some((point, output)) = point_and_output { + let original_position = pointer.current_location(); + pointer.set_location(point); + + let mut shell = self.common.shell.write(); + shell.update_pointer_position(point.as_global().to_local(&output), &output); + + let seat = shell + .seats + .iter() + .find(|s| s.get_pointer().as_ref() == Some(pointer)) + .cloned(); + + if let Some(seat) = seat { + shell.update_focal_point( + &seat, + original_position.as_global(), + self.common.config.cosmic_conf.accessibility_zoom.view_moves, + ); + + let output_geometry = output.geometry(); + for session in cursor_sessions_for_output(&shell, &output) { + if let Some((geometry, offset)) = seat.cursor_geometry( + point.to_buffer( + output.current_scale().fractional_scale(), + output.current_transform(), + &output_geometry.size.to_f64().as_logical(), + ), + self.common.clock.now(), + ) { + if session + .current_constraints() + .map(|constraint| constraint.size != geometry.size) + .unwrap_or(true) + { + session.update_constraints(BufferConstraints { + size: geometry.size, + shm: vec![ShmFormat::Argb8888], + dma: None, + }); + } + session.set_cursor_hotspot(offset); + session.set_cursor_pos(Some(geometry.loc)); + } + } + } + } + } } fn cursor_sessions_for_output<'a>( From 9702aae5235af9441b6bd39413a83bf73c4daf68 Mon Sep 17 00:00:00 2001 From: Skygrango Date: Wed, 13 May 2026 20:05:39 +0800 Subject: [PATCH 07/64] input: when updating pointer in the confined mode, recalibrate to the valid coordinates --- src/input/mod.rs | 188 +++++++++++++++++++++++++++++------------------ 1 file changed, 117 insertions(+), 71 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 4e1ba33e..747ad172 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -354,27 +354,6 @@ impl State { _ => {} }); } - let original_position = position; - position += event.delta().as_global(); - - let output = shell - .outputs() - .find(|output| output.geometry().to_f64().contains(position)) - .cloned() - .unwrap_or(current_output.clone()); - - let output_geometry = output.geometry(); - position.x = position.x.clamp( - output_geometry.loc.x as f64, - (output_geometry.loc.x + output_geometry.size.w - 1) as f64, - ); - position.y = position.y.clamp( - output_geometry.loc.y as f64, - (output_geometry.loc.y + output_geometry.size.h - 1) as f64, - ); - - let new_under = State::surface_under(position, &output, &shell) - .map(|(target, pos)| (target, pos.as_logical())); std::mem::drop(shell); ptr.relative_motion( @@ -392,6 +371,25 @@ impl State { return; } + let original_position = position; + position += event.delta().as_global(); + let shell = self.common.shell.read(); + let output = shell + .outputs() + .find(|output| output.geometry().to_f64().contains(position)) + .cloned() + .unwrap_or(current_output.clone()); + drop(shell); + let output_geometry = output.geometry(); + position.x = position.x.clamp( + output_geometry.loc.x as f64, + (output_geometry.loc.x + output_geometry.size.w - 1) as f64, + ); + position.y = position.y.clamp( + output_geometry.loc.y as f64, + (output_geometry.loc.y + output_geometry.size.h - 1) as f64, + ); + if ptr.is_grabbed() { if seat .user_data() @@ -499,50 +497,90 @@ impl State { } } - // If confined, don't move pointer if it would go outside surface or region - if pointer_confined && let Some((surface, surface_loc)) = &under { - if new_under.as_ref().and_then(|(under, _)| under.wl_surface()) - != surface.wl_surface() - { - ptr.frame(self); - return; - } - match surface { - PointerFocusTarget::WlSurface { surface, .. } => { - if under_from_surface_tree( - surface, - position.as_logical() - surface_loc.to_f64(), - (0, 0), - WindowSurfaceType::ALL, - ) - .is_none() - { - ptr.frame(self); - return; - } - } - PointerFocusTarget::X11Surface { surface, .. } => { - if surface - .surface_under( + // If confined, help user to update valid coordinates can improve user experience + let shell = self.common.shell.read(); + let new_under = if pointer_confined && let Some((surface, surface_loc)) = &under + { + let is_legal = |pos: Point, shell: &Shell| { + let new_under = State::surface_under(pos, &output, shell) + .map(|(target, pos)| (target, pos.as_logical())); + + //We should only check size, not the surface, so that we can move the mouse over the OSD in confined mode + // if new_under.as_ref().and_then(|(under, _)| under.wl_surface()) + // != surface.wl_surface() + // { + // return (false, None); + // } + + match surface { + PointerFocusTarget::WlSurface { surface, .. } => { + if under_from_surface_tree( + surface, position.as_logical() - surface_loc.to_f64(), (0, 0), WindowSurfaceType::ALL, ) .is_none() - { - ptr.frame(self); - return; + { + return (false, None); + } + } + PointerFocusTarget::X11Surface { surface, .. } => { + if surface + .surface_under( + position.as_logical() - surface_loc.to_f64(), + (0, 0), + WindowSurfaceType::ALL, + ) + .is_none() + { + return (false, None); + } + } + _ => {} + } + + if let Some(region) = &confine_region + && !region + .contains((pos.as_logical() - *surface_loc).to_i32_round()) + { + return (false, None); + } + (true, new_under) + }; + + match is_legal(position, &shell) { + (true, under) => under, + _ => { + let y_only_pos = Point::new(original_position.x, position.y); + let x_only_pos = Point::new(position.x, original_position.y); + match is_legal(y_only_pos, &shell) { + (true, under) => { + position = y_only_pos; + under + } + _ => match is_legal(x_only_pos, &shell) { + (true, under) => { + position = x_only_pos; + under + } + _ => { + position = original_position; + None + } + }, } } - _ => {} - } - if let Some(region) = confine_region - && !region - .contains((position.as_logical() - *surface_loc).to_i32_round()) - { - ptr.frame(self); - return; } + } else { + State::surface_under(position, &output, &shell) + .map(|(target, pos)| (target, pos.as_logical())) + }; + + drop(shell); + if pointer_confined && new_under.is_none() { + ptr.frame(self); + return; } let serial = SERIAL_COUNTER.next_serial(); @@ -557,24 +595,32 @@ impl State { ); ptr.frame(self); - // If pointer is now in a constraint region, activate it + // If pointer is now in a constraint region and window is in focused, activate it if let Some((under, surface_location)) = new_under .and_then(|(target, loc)| Some((target.wl_surface()?.into_owned(), loc))) { - with_pointer_constraint(&under, &ptr, |constraint| match constraint { - Some(constraint) if !constraint.is_active() => { - let region = match &*constraint { - PointerConstraint::Locked(locked) => locked.region(), - PointerConstraint::Confined(confined) => confined.region(), - }; - let point = - (ptr.current_location() - surface_location).to_i32_round(); - if region.is_none_or(|region| region.contains(point)) { - constraint.activate(); + let shell = self.common.shell.read(); + let is_focused = seat + .get_keyboard() + .and_then(|k| k.current_focus()) + .is_some_and(|f| f.has_surface(&shell, &under)); + + if is_focused { + with_pointer_constraint(&under, &ptr, |constraint| match constraint { + Some(constraint) if !constraint.is_active() => { + let region = match &*constraint { + PointerConstraint::Locked(locked) => locked.region(), + PointerConstraint::Confined(confined) => confined.region(), + }; + let point = + (ptr.current_location() - surface_location).to_i32_round(); + if region.is_none_or(|region| region.contains(point)) { + constraint.activate(); + } } - } - _ => {} - }); + _ => {} + }); + } } let mut shell = self.common.shell.write(); From 81066d437a9eb250793a9abc55fe26b8fa9fc00d Mon Sep 17 00:00:00 2001 From: Skygrango Date: Wed, 13 May 2026 20:06:33 +0800 Subject: [PATCH 08/64] shell: remove constraint when target changed --- src/shell/focus/mod.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index 58301072..e109519e 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -1,6 +1,6 @@ use crate::{ shell::{CosmicSurface, MinimizedWindow, Shell, Trigger, element::CosmicMapped}, - state::Common, + state::{Common, State}, utils::prelude::*, wayland::handlers::{xdg_shell::PopupGrabData, xwayland_keyboard_grab::XWaylandGrabSeatData}, }; @@ -12,6 +12,7 @@ use smithay::{ reexports::wayland_server::{Resource, protocol::wl_surface::WlSurface}, utils::{IsAlive, Point, SERIAL_COUNTER, Serial}, wayland::{ + pointer_constraints::with_pointer_constraint, seat::WaylandFocus, selection::{data_device::set_data_device_focus, primary_selection::set_primary_focus}, shell::wlr_layer::{KeyboardInteractivity, Layer}, @@ -347,6 +348,20 @@ fn update_focus_state( ) { // update keyboard focus if let Some(keyboard) = seat.get_keyboard() { + // remove constraint when target changed + let old_focus = keyboard.current_focus(); + if let Some(old_target) = old_focus + && target != Some(&old_target) + && let Some(surface) = old_target.wl_surface() + && let Some(pointer) = seat.get_pointer() + { + with_pointer_constraint(&surface, &pointer, |constraint| { + if let Some(constraint) = constraint { + constraint.deactivate(); + } + }); + } + if should_update_cursor && state.common.config.cosmic_conf.cursor_follows_focus && target.is_some() From 4fb7d493160a1aa3d21e6a61c5aa047c730f446d Mon Sep 17 00:00:00 2001 From: Skygrango Date: Fri, 22 May 2026 00:32:31 +0800 Subject: [PATCH 09/64] chore: update smithay --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f8b41976..22c0e63c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4924,7 +4924,7 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/smithay/smithay.git?rev=774f2ab#774f2ab010e080a2f79176d0e12eb63d5e09d680" +source = "git+https://github.com/smithay/smithay.git?rev=df73da7#df73da7183c365b00eb4558690a48061462053f2" dependencies = [ "aliasable", "appendlist", diff --git a/Cargo.toml b/Cargo.toml index ee957f8d..a63e3be0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -143,4 +143,4 @@ cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch cosmic-client-toolkit = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } [patch.crates-io] -smithay = { git = "https://github.com/smithay/smithay.git", rev = "774f2ab" } +smithay = { git = "https://github.com/smithay/smithay.git", rev = "df73da7" } From 208c2128cdc062847d4a51f71b5c4d55add04709 Mon Sep 17 00:00:00 2001 From: Skygrango Date: Wed, 13 May 2026 20:08:21 +0800 Subject: [PATCH 10/64] wayland: implement new_constraint, remove_constraint and cursor_position_hint for PointerConstraintsHandler new_constraint: check if the surface has pointer focus, keyboard focus, and if the pointer is within toplevel geometry or constraint region. If these conditions are met, then enable the constraint. remove_constraint: apply last cursor position hint that saved in seat. cursor_position_hint: save the cursor position hints provided by the client to the seat. --- src/input/mod.rs | 21 ++-- src/shell/mod.rs | 29 ++--- src/shell/seats.rs | 30 ++++- src/wayland/handlers/pointer_constraints.rs | 118 +++++++++++++++++--- 4 files changed, 156 insertions(+), 42 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 747ad172..cf08a721 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -505,12 +505,12 @@ impl State { let new_under = State::surface_under(pos, &output, shell) .map(|(target, pos)| (target, pos.as_logical())); - //We should only check size, not the surface, so that we can move the mouse over the OSD in confined mode - // if new_under.as_ref().and_then(|(under, _)| under.wl_surface()) - // != surface.wl_surface() - // { - // return (false, None); - // } + // TODO: We might need a solution that allows constraints to bypass the surface without affecting the constraints themselves + if new_under.as_ref().and_then(|(under, _)| under.wl_surface()) + != surface.wl_surface() + { + return (false, None); + } match surface { PointerFocusTarget::WlSurface { surface, .. } => { @@ -2375,11 +2375,10 @@ impl State { pointer: &PointerHandle, mut location: Point, ) { - if let Some(client) = surface.client() { - let scale = self.client_compositor_state(&client).client_scale(); - location.x /= scale; - location.y /= scale; - } + let Some(client) = surface.client() else { + return; + }; + location = location.downscale(self.client_compositor_state(&client).client_scale()); let point_and_output = { let shell = self.common.shell.read(); diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 061be6b4..66c3791f 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -737,22 +737,23 @@ impl WorkspaceSet { surface: &WlSurface, ) -> Option<(Rectangle, Point)> { let mut root = surface.clone(); - loop { - while let Some(parent) = get_parent(&root) { + + while let Some(parent) = get_parent(&root) { + root = parent; + } + + while smithay::wayland::compositor::get_role(&root) == Some(XDG_POPUP_ROLE) { + let parent = with_states(&root, |states| { + states + .data_map + .get::() + .and_then(|m| m.lock().unwrap().parent.as_ref().cloned()) + }); + if let Some(parent) = parent { root = parent; + } else { + break; } - if smithay::wayland::compositor::get_role(&root) == Some(XDG_POPUP_ROLE) - && let Some(parent) = with_states(&root, |states| { - states - .data_map - .get::() - .and_then(|m| m.lock().unwrap().parent.as_ref().cloned()) - }) - { - root = parent; - continue; - } - break; } self.sticky_layer diff --git a/src/shell/seats.rs b/src/shell/seats.rs index a4f14364..5273e97f 100644 --- a/src/shell/seats.rs +++ b/src/shell/seats.rs @@ -17,8 +17,11 @@ use smithay::{ pointer::{CursorImageAttributes, CursorImageStatus}, }, output::Output, - reexports::{input::Device as InputDevice, wayland_server::DisplayHandle}, - utils::{Buffer, IsAlive, Monotonic, Point, Rectangle, Serial, Time, Transform}, + reexports::{ + input::Device as InputDevice, + wayland_server::{DisplayHandle, protocol::wl_surface::WlSurface}, + }, + utils::{Buffer, IsAlive, Logical, Monotonic, Point, Rectangle, Serial, Time, Transform}, wayland::compositor::with_states, }; use tracing::warn; @@ -178,6 +181,9 @@ struct ActiveOutput(pub Mutex); /// The output which currently has keyboard focus struct FocusedOutput(pub Mutex>); +#[derive(Default)] +pub struct PointerConstraintHint(pub Mutex)>>); + #[derive(Default)] pub struct LastModifierChange(pub Mutex>); @@ -201,6 +207,7 @@ pub fn create_seat( userdata.insert_if_missing_threadsafe(CursorState::default); userdata.insert_if_missing_threadsafe(|| ActiveOutput(Mutex::new(output.clone()))); userdata.insert_if_missing_threadsafe(|| FocusedOutput(Mutex::new(None))); + userdata.insert_if_missing_threadsafe(PointerConstraintHint::default); userdata.insert_if_missing_threadsafe(|| Mutex::new(CursorImageStatus::default_named())); // A lot of clients bind keyboard and pointer unconditionally once on launch.. @@ -252,6 +259,8 @@ pub trait SeatExt { fn supressed_buttons(&self) -> &SupressedButtons; fn modifiers_shortcut_queue(&self) -> &ModifiersShortcutQueue; fn last_modifier_change(&self) -> Option; + fn pointer_constraint_hint(&self) -> Option<(WlSurface, Point)>; + fn set_pointer_constraint_hint(&self, hint: Option<(WlSurface, Point)>); fn cursor_geometry( &self, @@ -337,6 +346,23 @@ impl SeatExt for Seat { .unwrap() } + fn pointer_constraint_hint(&self) -> Option<(WlSurface, Point)> { + let lock = self.user_data().get::().unwrap(); + let mut hint = lock.0.lock().unwrap(); + // Check if alive + if let Some((ref surface, _)) = *hint + && !surface.alive() + { + *hint = None; + } + hint.clone() + } + + fn set_pointer_constraint_hint(&self, hint: Option<(WlSurface, Point)>) { + let lock = self.user_data().get::().unwrap(); + *lock.0.lock().unwrap() = hint; + } + fn cursor_geometry( &self, loc: impl Into>, diff --git a/src/wayland/handlers/pointer_constraints.rs b/src/wayland/handlers/pointer_constraints.rs index 71e4a4d1..e17c1961 100644 --- a/src/wayland/handlers/pointer_constraints.rs +++ b/src/wayland/handlers/pointer_constraints.rs @@ -1,35 +1,123 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::state::State; +use crate::{shell::CosmicSurface, state::State, utils::prelude::*}; use smithay::{ input::pointer::PointerHandle, reexports::wayland_server::protocol::wl_surface::WlSurface, utils::{Logical, Point}, - wayland::{ - pointer_constraints::{PointerConstraintsHandler, with_pointer_constraint}, - seat::WaylandFocus, - }, + wayland::{pointer_constraints::PointerConstraintsHandler, seat::WaylandFocus}, }; +pub use smithay::wayland::pointer_constraints::{PointerConstraintRef, with_pointer_constraint}; + impl PointerConstraintsHandler for State { fn new_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle) { - // XXX region - if pointer - .current_focus() - .is_some_and(|x| x.wl_surface().as_deref() == Some(surface)) - { + let seat = self + .common + .shell + .read() + .seats + .iter() + .find(|s| s.get_pointer().as_ref() == Some(pointer)) + .cloned(); + + let (is_under, is_focused, surface_location) = if let Some(seat) = seat { + seat.set_pointer_constraint_hint(None); + let current_output = seat.active_output(); + let position = seat.get_pointer().unwrap().current_location().as_global(); + let shell = self.common.shell.read(); + let under = State::surface_under(position, ¤t_output, &shell); + let mut surface_location = None; + + let is_under = if let Some((target, target_loc)) = under + && let Some(under_surface) = target.wl_surface() + { + if *under_surface == *surface { + surface_location = Some(target_loc); + true + } else { + CosmicSurface::surface_tree_offset(surface, &under_surface).is_some_and( + |offset| { + surface_location = Some(target_loc - offset.to_f64().as_global()); + true + }, + ) + } + } else { + false + }; + + let is_focused = seat + .get_keyboard() + .and_then(|k| k.current_focus()) + .is_some_and(|f| f.has_surface(&shell, surface)); + + (is_under, is_focused, surface_location) + } else { + (false, false, None) + }; + + if is_focused && is_under { with_pointer_constraint(surface, pointer, |constraint| { - constraint.unwrap().activate(); + if let Some(constraint) = constraint { + if let Some(region) = constraint.region() { + if let Some(surface_location) = surface_location + && let position = pointer.current_location() + && let point = (position - surface_location.as_logical()).to_i32_round() + && region.contains(point) + { + constraint.activate(); + } + } else { + constraint.activate(); + } + } }); } } + fn remove_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle) { + if with_pointer_constraint(surface, pointer, |constraint| constraint.is_none()) { + let seat = self + .common + .shell + .read() + .seats + .iter() + .find(|s| s.get_pointer().as_ref() == Some(pointer)) + .cloned(); + + if let Some(seat) = seat + && let Some((hint_surface, hint_location)) = seat.pointer_constraint_hint() + && hint_surface == *surface + { + self.apply_cursor_hint(surface, pointer, hint_location); + seat.set_pointer_constraint_hint(None); + } + } + } + fn cursor_position_hint( &mut self, - _surface: &WlSurface, - _pointer: &PointerHandle, - _location: Point, + surface: &WlSurface, + pointer: &PointerHandle, + location: Point, ) { - // TODO + if with_pointer_constraint(surface, pointer, |constraint| { + constraint.is_some_and(|c| c.is_active()) + }) { + let seat = self + .common + .shell + .read() + .seats + .iter() + .find(|s| s.get_pointer().as_ref() == Some(pointer)) + .cloned(); + + if let Some(seat) = seat { + seat.set_pointer_constraint_hint(Some((surface.clone(), location))); + } + } } } From 203cad8cbd874807c19d98da3beb6e6730a57198 Mon Sep 17 00:00:00 2001 From: Skygrango Date: Wed, 27 May 2026 14:17:01 +0800 Subject: [PATCH 11/64] shell: disable WindowUI detection if constraint is active --- src/input/mod.rs | 15 ++++++--------- src/shell/element/mod.rs | 5 ++++- src/shell/element/window.rs | 20 +++++++++++++++++--- src/shell/grabs/moving.rs | 1 + src/shell/layout/floating/mod.rs | 13 ++++++++++++- src/shell/layout/tiling/mod.rs | 9 +++++++++ src/shell/mod.rs | 8 ++++++-- src/shell/workspace.rs | 22 ++++++++++++++-------- 8 files changed, 69 insertions(+), 24 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index cf08a721..1a26dc62 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -2168,14 +2168,14 @@ impl State { } Stage::StickyPopups(layout) => { if let Some(element) = - layout.popup_element_under(global_pos.to_local(output)) + layout.popup_element_under(global_pos.to_local(output), seat) { return ControlFlow::Break(Ok(Some(element))); } } Stage::Sticky(layout) => { if let Some(element) = - layout.toplevel_element_under(global_pos.to_local(output)) + layout.toplevel_element_under(global_pos.to_local(output), seat) { return ControlFlow::Break(Ok(Some(element))); } @@ -2330,7 +2330,7 @@ impl State { } Stage::StickyPopups(floating_layer) => { if let Some(under) = floating_layer - .popup_surface_under(relative_pos) + .popup_surface_under(relative_pos, seat) .map(|(target, point)| (target, point.to_global(output))) { return ControlFlow::Break(Ok(Some(under))); @@ -2338,7 +2338,7 @@ impl State { } Stage::Sticky(floating_layer) => { if let Some(under) = floating_layer - .toplevel_surface_under(relative_pos) + .toplevel_surface_under(relative_pos, seat) .map(|(target, point)| (target, point.to_global(output))) { return ControlFlow::Break(Ok(Some(under))); @@ -2392,11 +2392,8 @@ impl State { let window_size = geometry.size.to_f64(); let is_legal = |p: Point| { - let in_window = p.x >= 0.0 - && p.y >= 0.0 - // hack: prevent the cursor from touching the edge of the window - && p.x <= window_size.w - 1. - && p.y <= window_size.h - 1.; + let in_window = + p.x >= 0.0 && p.y >= 0.0 && p.x < window_size.w && p.y < window_size.h; if !in_window { return false; } diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index d5b79703..f58259bf 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -269,10 +269,13 @@ impl CosmicMapped { &self, relative_pos: Point, surface_type: WindowSurfaceType, + seat: &Seat, ) -> Option<(PointerFocusTarget, Point)> { match &self.element { CosmicMappedInternal::Stack(stack) => stack.focus_under(relative_pos, surface_type), - CosmicMappedInternal::Window(window) => window.focus_under(relative_pos, surface_type), + CosmicMappedInternal::Window(window) => { + window.focus_under(relative_pos, surface_type, Some(seat)) + } _ => unreachable!(), } } diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 32ca4d33..dd45de4b 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -57,7 +57,7 @@ use smithay::{ Buffer, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, Transform, user_data::UserDataMap, }, - wayland::seat::WaylandFocus, + wayland::{pointer_constraints::with_pointer_constraint, seat::WaylandFocus}, }; use std::{ borrow::Cow, @@ -274,12 +274,25 @@ impl CosmicWindow { &self, mut relative_pos: Point, surface_type: WindowSurfaceType, + seat: Option<&Seat>, ) -> Option<(PointerFocusTarget, Point)> { + let has_constraint = if let Some(seat) = seat + && let Some(pointer) = seat.get_pointer() + && let Some(surface) = self.wl_surface() + && with_pointer_constraint(&surface, &pointer, |constraint| { + constraint.is_some_and(|c| c.is_active()) + }) { + true + } else { + false + }; + self.0.with_program(|p| { let mut offset = Point::from((0., 0.)); let mut window_ui = None; let has_ssd = p.has_ssd(false); - if (has_ssd || p.has_tiled_state()) + + if (!has_constraint && (has_ssd || p.has_tiled_state())) && surface_type.contains(WindowSurfaceType::TOPLEVEL) { let geo = p.window.geometry(); @@ -877,7 +890,8 @@ impl SpaceElement for CosmicWindow { }) } fn is_in_input_region(&self, point: &Point) -> bool { - self.focus_under(*point, WindowSurfaceType::ALL).is_some() + self.focus_under(*point, WindowSurfaceType::ALL, None) + .is_some() } fn set_activate(&self, activated: bool) { if self diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index ad5915f9..c92c5bf4 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -953,6 +953,7 @@ impl Drop for MoveGrab { if let Some((target, offset)) = mapped.focus_under( current_location - position.as_logical().to_f64(), WindowSurfaceType::ALL, + &seat, ) { pointer.motion( state, diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index e399f356..81c7a4da 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -731,7 +731,11 @@ impl FloatingLayout { self.space.element_geometry(elem).map(RectExt::as_local) } - pub fn popup_element_under(&self, location: Point) -> Option { + pub fn popup_element_under( + &self, + location: Point, + seat: &Seat, + ) -> Option { self.space .elements() .rev() @@ -752,6 +756,7 @@ impl FloatingLayout { if e.focus_under( point.as_logical(), WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE, + seat, ) .is_some() { @@ -765,6 +770,7 @@ impl FloatingLayout { pub fn toplevel_element_under( &self, location: Point, + seat: &Seat, ) -> Option { self.space .elements() @@ -786,6 +792,7 @@ impl FloatingLayout { if e.focus_under( point.as_logical(), WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE, + seat, ) .is_some() { @@ -799,6 +806,7 @@ impl FloatingLayout { pub fn popup_surface_under( &self, location: Point, + seat: &Seat, ) -> Option<(PointerFocusTarget, Point)> { self.space .elements() @@ -820,6 +828,7 @@ impl FloatingLayout { e.focus_under( point.as_logical(), WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE, + seat, ) .map(|(surface, surface_offset)| { (surface, render_location + surface_offset.as_local()) @@ -830,6 +839,7 @@ impl FloatingLayout { pub fn toplevel_surface_under( &self, location: Point, + seat: &Seat, ) -> Option<(PointerFocusTarget, Point)> { self.space .elements() @@ -851,6 +861,7 @@ impl FloatingLayout { e.focus_under( point.as_logical(), WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE, + seat, ) .map(|(surface, surface_offset)| { (surface, render_location + surface_offset.as_local()) diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 22d592bf..e7a7f0f1 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -3145,6 +3145,7 @@ impl TilingLayout { pub fn popup_element_under( &self, location_f64: Point, + seat: &Seat, ) -> Option { let location = location_f64.to_i32_round(); @@ -3157,6 +3158,7 @@ impl TilingLayout { .focus_under( (location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(), WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE, + seat, ) .is_some() { @@ -3170,6 +3172,7 @@ impl TilingLayout { pub fn toplevel_element_under( &self, location_f64: Point, + seat: &Seat, ) -> Option { let location = location_f64.to_i32_round(); @@ -3182,6 +3185,7 @@ impl TilingLayout { .focus_under( (location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(), WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE, + seat, ) .is_some() { @@ -3196,6 +3200,7 @@ impl TilingLayout { &self, location_f64: Point, overview: OverviewMode, + seat: &Seat, ) -> Option<(PointerFocusTarget, Point)> { let location = location_f64.to_i32_round(); @@ -3210,6 +3215,7 @@ impl TilingLayout { if let Some((target, surface_offset)) = mapped.focus_under( (location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(), WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE, + seat, ) { return Some(( target, @@ -3227,6 +3233,7 @@ impl TilingLayout { &self, location_f64: Point, overview: OverviewMode, + seat: &Seat, ) -> Option<(PointerFocusTarget, Point)> { let tree = &self.queue.trees.back().unwrap().0; let root = tree.root_node_id()?; @@ -3243,6 +3250,7 @@ impl TilingLayout { if let Some((target, surface_offset)) = mapped.focus_under( (location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(), WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE, + seat, ) { return Some(( target, @@ -3293,6 +3301,7 @@ impl TilingLayout { .focus_under( test_point, WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE, + seat, ) .map(|(surface, surface_offset)| { ( diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 66c3791f..6ea3b5e2 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -3714,7 +3714,11 @@ impl Shell { if old_mapped.is_maximized(false) { new_mapped.set_maximized(false); } - start_data.set_focus(new_mapped.focus_under((0., 0.).into(), WindowSurfaceType::ALL)); + start_data.set_focus(new_mapped.focus_under( + (0., 0.).into(), + WindowSurfaceType::ALL, + seat, + )); new_mapped } else { old_mapped.clone() @@ -4185,7 +4189,7 @@ impl Shell { let element_offset = (new_loc - geometry.loc).as_logical(); let focus = mapped - .focus_under(element_offset.to_f64(), WindowSurfaceType::ALL) + .focus_under(element_offset.to_f64(), WindowSurfaceType::ALL, seat) .map(|(target, surface_offset)| (target, (surface_offset + element_offset.to_f64()))); start_data.set_location(new_loc.as_logical().to_f64()); start_data.set_focus(focus.clone()); diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index a79b2acb..ccf5e4ab 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -791,8 +791,8 @@ impl Workspace { } self.floating_layer - .popup_element_under(location) - .or_else(|| self.tiling_layer.popup_element_under(location)) + .popup_element_under(location, seat) + .or_else(|| self.tiling_layer.popup_element_under(location, seat)) .or_else(|| { if last_focused.is_none_or(|t| !matches!(t, FocusTarget::Fullscreen(_))) && let Some(fullscreen) = self.fullscreen.as_ref() @@ -841,8 +841,8 @@ impl Workspace { } self.floating_layer - .toplevel_element_under(location) - .or_else(|| self.tiling_layer.toplevel_element_under(location)) + .toplevel_element_under(location, seat) + .or_else(|| self.tiling_layer.toplevel_element_under(location, seat)) .or_else(|| { if last_focused.is_none_or(|t| !matches!(t, FocusTarget::Fullscreen(_))) && let Some(fullscreen) = self.fullscreen.as_ref() @@ -895,8 +895,11 @@ impl Workspace { .as_ref() .filter(|f| last_focused.is_some_and(|t| t == &f.surface)) .and_then(check_fullscreen) - .or_else(|| self.floating_layer.popup_surface_under(location)) - .or_else(|| self.tiling_layer.popup_surface_under(location, overview)) + .or_else(|| self.floating_layer.popup_surface_under(location, seat)) + .or_else(|| { + self.tiling_layer + .popup_surface_under(location, overview, seat) + }) .or_else(|| { self.fullscreen .as_ref() @@ -941,8 +944,11 @@ impl Workspace { .as_ref() .filter(|f| last_focused.is_some_and(|t| t == &f.surface)) .and_then(check_fullscreen) - .or_else(|| self.floating_layer.toplevel_surface_under(location)) - .or_else(|| self.tiling_layer.toplevel_surface_under(location, overview)) + .or_else(|| self.floating_layer.toplevel_surface_under(location, seat)) + .or_else(|| { + self.tiling_layer + .toplevel_surface_under(location, overview, seat) + }) .or_else(|| { self.fullscreen .as_ref() From 9f28a764a7cac5f9acd16a6cff46d72d0656c87d Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 28 May 2026 14:53:05 -0700 Subject: [PATCH 12/64] chore: update smithay Includes the fix in https://github.com/Smithay/smithay/pull/2038, which helps addresses a regression in controller input for Steam games. --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/shell/element/surface.rs | 4 ++-- src/shell/focus/order.rs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 22c0e63c..029590c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4924,7 +4924,7 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/smithay/smithay.git?rev=df73da7#df73da7183c365b00eb4558690a48061462053f2" +source = "git+https://github.com/smithay/smithay.git?rev=85f83ab#85f83ab61711e725aa1f3481df4f4dc5370e925b" dependencies = [ "aliasable", "appendlist", diff --git a/Cargo.toml b/Cargo.toml index a63e3be0..9e74d6ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -143,4 +143,4 @@ cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch cosmic-client-toolkit = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } [patch.crates-io] -smithay = { git = "https://github.com/smithay/smithay.git", rev = "df73da7" } +smithay = { git = "https://github.com/smithay/smithay.git", rev = "85f83ab" } diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 769ffbd9..f715bba3 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -345,7 +345,7 @@ impl CosmicSurface { state.is_some_and(|state| state.states.contains(ToplevelState::Resizing)) })) } - WindowSurface::X11(surface) => surface.pending_geometry().map(|_| true), + WindowSurface::X11(surface) => surface.pending_configure().map(|_| true), } } @@ -591,7 +591,7 @@ impl CosmicSurface { } }) } - WindowSurface::X11(surface) => surface.pending_geometry().is_none(), + WindowSurface::X11(surface) => surface.pending_configure().is_none(), } } diff --git a/src/shell/focus/order.rs b/src/shell/focus/order.rs index 6a2488fe..8c6dae0b 100644 --- a/src/shell/focus/order.rs +++ b/src/shell/focus/order.rs @@ -220,12 +220,12 @@ fn render_input_order_internal( .rev() .filter(|or| { (*or) - .geometry() + .last_configure() .as_global() .intersection(output.geometry()) .is_some() }) - .map(|or| (or, or.geometry().loc.as_global())) + .map(|or| (or, or.last_configure().loc.as_global())) { callback(Stage::OverrideRedirect { surface, location })?; } From 28ef6bdbc8f5a9040346a1a8f7293cbbdb2943bc Mon Sep 17 00:00:00 2001 From: Hojjat Abdollahi Date: Tue, 2 Jun 2026 09:48:33 -0600 Subject: [PATCH 13/64] feat: support multiple fullscreen windows per workspace --- src/backend/kms/surface/mod.rs | 41 +- src/input/mod.rs | 12 +- src/shell/focus/mod.rs | 24 +- src/shell/focus/order.rs | 4 +- src/shell/mod.rs | 128 +++--- src/shell/workspace.rs | 412 ++++++++++++-------- src/state.rs | 22 +- src/wayland/handlers/dmabuf.rs | 8 +- src/wayland/handlers/toplevel_management.rs | 2 +- src/wayland/handlers/xdg_activation.rs | 8 +- src/wayland/handlers/xdg_shell/popup.rs | 2 +- src/xwayland.rs | 26 +- 12 files changed, 381 insertions(+), 308 deletions(-) diff --git a/src/backend/kms/surface/mod.rs b/src/backend/kms/surface/mod.rs index 6b0d42b4..62ba7c1f 100644 --- a/src/backend/kms/surface/mod.rs +++ b/src/backend/kms/surface/mod.rs @@ -1024,14 +1024,18 @@ impl SurfaceThreadState { let animations_going = shell.animations_going(); let output = self.mirroring.as_ref().unwrap_or(&self.output); if let Some((_, workspace)) = shell.workspaces.active(output) { - if let Some(fullscreen_surface) = workspace.get_fullscreen() { + let seat = shell.seats.last_active(); + if let Some(fullscreen_surface) = workspace.get_fullscreen(seat) { const _30_FPS: Duration = Duration::from_nanos(1_000_000_000 / 30); ( true, - fullscreen_surface.wl_surface().is_some_and(|surface| { - recursive_frame_time_estimation(&self.clock, &surface) - .is_some_and(|dur| dur <= _30_FPS) - }), + fullscreen_surface + .surface + .wl_surface() + .is_some_and(|surface| { + recursive_frame_time_estimation(&self.clock, &surface) + .is_some_and(|dur| dur <= _30_FPS) + }), animations_going, ) } else { @@ -1490,18 +1494,21 @@ fn render_node_for_output( let Some(workspace) = shell.active_space(output) else { return *target_node; }; - let nodes = workspace - .get_fullscreen() - .map(|w| vec![w.clone()]) - .unwrap_or_else(|| { - workspace - .mapped() - .map(|mapped| mapped.active_window()) - .collect::>() - }) - .into_iter() - .flat_map(|w| w.wl_surface().and_then(|s| source_node_for_surface(&s))) - .collect::>(); + let fullscreens: Vec<_> = workspace + .get_fullscreen_surfaces() + .map(|f| f.surface.clone()) + .collect(); + let nodes = if !fullscreens.is_empty() { + fullscreens + } else { + workspace + .mapped() + .map(|mapped| mapped.active_window()) + .collect::>() + } + .into_iter() + .flat_map(|w| w.wl_surface().and_then(|s| source_node_for_surface(&s))) + .collect::>(); if nodes.contains(target_node) || nodes.is_empty() { *target_node diff --git a/src/input/mod.rs b/src/input/mod.rs index 1a26dc62..c4f422c3 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -2498,16 +2498,14 @@ fn cursor_sessions_for_output<'a>( .active_space(output) .into_iter() .flat_map(|workspace| { - let maybe_fullscreen = workspace.get_fullscreen(); + let fullscreen_cursors: Vec<_> = workspace + .get_fullscreen_surfaces() + .flat_map(|f| f.surface.cursor_sessions()) + .collect(); workspace .cursor_sessions() .into_iter() - .chain( - maybe_fullscreen - .map(|w| w.cursor_sessions()) - .into_iter() - .flatten(), - ) + .chain(fullscreen_cursors) .chain(output.cursor_sessions()) }) } diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index e109519e..9374230a 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -288,22 +288,18 @@ impl Shell { } let workspace = &mut set.workspaces[set.active]; - if let Some(fullscreen) = workspace.get_fullscreen() { - if self.seats.iter().any(|seat| { + for fs in workspace.get_fullscreen_surfaces() { + let is_focused = self.seats.iter().any(|seat| { if let Some(KeyboardFocusTarget::Fullscreen(s)) = seat.get_keyboard().unwrap().current_focus() { - &s == fullscreen + s == fs.surface } else { false } - }) { - fullscreen.set_activated(true); - fullscreen.send_configure(); - } else { - fullscreen.set_activated(false); - fullscreen.send_configure(); - } + }); + fs.surface.set_activated(is_focused); + fs.surface.send_configure(); } for focused in focused_windows.iter() { raise_with_children(&mut workspace.floating_layer, focused); @@ -661,7 +657,9 @@ fn focus_target_is_valid( .has_node(&node), KeyboardFocusTarget::Fullscreen(window) => { let workspace = shell.active_space(output).unwrap(); - workspace.get_fullscreen().is_some_and(|w| w == &window) + workspace + .get_fullscreen_surfaces() + .any(|f| f.surface == window) } KeyboardFocusTarget::Popup(_) => true, KeyboardFocusTarget::LockSurface(_) => false, @@ -714,9 +712,9 @@ fn update_focus_target( .map(KeyboardFocusTarget::Element) .or_else(|| { workspace - .get_fullscreen() + .get_fullscreen(seat) .cloned() - .map(KeyboardFocusTarget::Fullscreen) + .map(|fs| KeyboardFocusTarget::Fullscreen(fs.surface)) }) }) } diff --git a/src/shell/focus/order.rs b/src/shell/focus/order.rs index 8c6dae0b..6d021e6c 100644 --- a/src/shell/focus/order.rs +++ b/src/shell/focus/order.rs @@ -112,8 +112,8 @@ fn render_input_order_internal( let output_size = output.geometry().size; // this is more hacky than I would like.. - let fullscreen = workspace.fullscreen.as_ref().filter(|f| !f.is_animating()); let seat = shell.seats.last_active(); + let fullscreen = workspace.get_fullscreen(seat); let is_active_workspace = seat.focused_output().is_some_and(|output| { shell .active_space(&output) @@ -144,7 +144,7 @@ fn render_input_order_internal( let Some(workspace) = shell.workspaces.space_for_handle(previous) else { return ControlFlow::Break(Err(OutputNoMode)); }; - let has_fullscreen = workspace.fullscreen.is_some(); + let has_fullscreen = workspace.get_fullscreen(seat).is_some(); let (forward, percentage) = match start { WorkspaceDelta::Shortcut(st) => ( diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 6ea3b5e2..da168360 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -763,21 +763,19 @@ impl WorkspaceSet { .any(|(w, _)| w.wl_surface().as_deref() == Some(&root)) }) .and_then(|w| { - w.surface_offset(surface).and_then(|offset| { - self.sticky_layer - .element_geometry(w) - .map(|geom| (geom, offset)) - }) + self.sticky_layer + .element_geometry(w) + .zip(w.surface_offset(surface)) }) .or_else(|| { self.workspaces.iter().find_map(|workspace| { workspace - .get_fullscreen() - .and_then(|fullscreen| { - (fullscreen.wl_surface().as_deref() == Some(&root)) + .get_fullscreen_surfaces() + .find_map(|fs| { + (fs.surface.wl_surface().as_deref() == Some(&root)) .then(|| { - fullscreen.surface_offset(surface).and_then(|offset| { - workspace.fullscreen_geometry().map(|geom| (geom, offset)) + fs.surface.surface_offset(surface).map(|offset| { + (workspace.fullscreen_geometry_for(fs), offset) }) }) .flatten() @@ -787,9 +785,7 @@ impl WorkspaceSet { w.windows() .any(|(w, _)| w.wl_surface().as_deref() == Some(&root)) .then(|| { - w.surface_offset(surface).and_then(|offset| { - workspace.element_geometry(w).map(|geom| (geom, offset)) - }) + workspace.element_geometry(w).zip(w.surface_offset(surface)) }) .flatten() }) @@ -1641,12 +1637,13 @@ impl Common { if let Some(mapped) = shell.element_for_surface(surface) { mapped.on_commit(surface); } - if let Some(surface) = shell + if let Some(fs) = shell .workspaces .spaces() - .find_map(|w| w.get_fullscreen().filter(|s| *s == surface)) + .flat_map(|w| w.get_fullscreen_surfaces()) + .find(|f| f.surface == *surface) { - surface.on_commit() + fs.surface.on_commit() }; } self.popups.commit(surface); @@ -1907,7 +1904,9 @@ impl Shell { .outputs() .find(|output| { let workspace = self.active_space(output).unwrap(); - workspace.get_fullscreen() == Some(&elem) + workspace + .get_fullscreen_surfaces() + .any(|f| f.surface == elem) }) .cloned(), KeyboardFocusTarget::Group(WindowGroup { node, .. }) => self @@ -2040,8 +2039,8 @@ impl Shell { let workspace = self.active_space(o).unwrap(); workspace - .get_fullscreen() - .is_some_and(|s| s.has_surface(surface, WindowSurfaceType::ALL)) + .get_fullscreen_surfaces() + .any(|f| f.surface.has_surface(surface, WindowSurfaceType::ALL)) || workspace .mapped() .any(|e| e.has_surface(surface, WindowSurfaceType::ALL)) @@ -2096,8 +2095,8 @@ impl Shell { .workspaces .spaces() .find(|w| { - w.get_fullscreen() - .is_some_and(|s| s.has_surface(surface, WindowSurfaceType::ALL)) + w.get_fullscreen_surfaces() + .any(|f| f.surface.has_surface(surface, WindowSurfaceType::ALL)) || w.mapped() .any(|e| e.has_surface(surface, WindowSurfaceType::ALL)) || w.minimized_windows.iter().any(|m| { @@ -2144,7 +2143,7 @@ impl Shell { .mapped() .any(|m| m.windows().any(|(s, _)| &s == surface)) || set.workspaces.iter().any(|w| { - w.get_fullscreen().is_some_and(|s| s == surface) + w.get_fullscreen_surfaces().any(|f| &f.surface == surface) || w.minimized_windows .iter() .any(|m| m.windows().any(|s| &s == surface)) @@ -2833,12 +2832,7 @@ impl Shell { let floating_exception = layout::has_floating_exception(&self.tiling_exceptions, &window); if should_be_fullscreen { - if let Some((surface, state, _)) = workspace.map_fullscreen(&window, &seat, None, None) - { - toplevel_leave_output(&surface, &workspace.output); - toplevel_leave_workspace(&surface, &workspace.handle); - self.remap_unfullscreened_window(surface, state, loop_handle); - } + workspace.map_fullscreen(&window, &seat, None, None); if was_activated { workspace_state.add_workspace_state(&workspace_handle, WState::Urgent); } @@ -3209,9 +3203,12 @@ impl Shell { let from_workspace = self.workspaces.space_for_handle_mut(from).unwrap(); // checked above let is_minimized = window.is_minimized(); - let is_fullscreen = from_workspace.get_fullscreen().is_some_and(|f| f == window); + let is_fullscreen = from_workspace + .get_fullscreen_surfaces() + .any(|f| &f.surface == window); let mut window_state = if is_fullscreen { - let (_, previous_state, previous_geometry) = from_workspace.take_fullscreen().unwrap(); + let (_, previous_state, previous_geometry) = + from_workspace.take_fullscreen(window).unwrap(); WorkspaceRestoreData::Fullscreen(previous_state.zip(previous_geometry).map( |(previous_state, previous_geometry)| FullscreenRestoreData { previous_state, @@ -3367,14 +3364,12 @@ impl Shell { ); mapped.into() } else if let WorkspaceRestoreData::Fullscreen(previous) = window_state { - if let Some((old_surface, previous_state, _)) = to_workspace.map_fullscreen( + to_workspace.map_fullscreen( window, None, previous.clone().map(|p| p.previous_state), previous.map(|p| p.previous_geometry), - ) { - self.remap_unfullscreened_window(old_surface, previous_state, evlh); - } + ); window.clone().into() } else { unreachable!() // TODO: sticky @@ -3592,8 +3587,12 @@ impl Shell { } else if let Some((workspace, output)) = self.workspace_for_surface(surface) { let workspace = self.workspaces.space_for_handle(&workspace).unwrap(); - if let Some(window) = workspace.get_fullscreen().filter(|s| *s == surface) { - let global_position = (workspace.fullscreen_geometry().unwrap().loc + if let Some(fs) = workspace + .get_fullscreen_surfaces() + .find(|f| &f.surface == surface) + { + let window = &fs.surface; + let global_position = (workspace.fullscreen_geometry_for(fs).loc + location.as_local()) .to_global(&output); @@ -3686,10 +3685,14 @@ impl Shell { let maybe_fullscreen_workspace = self .workspaces .spaces_mut() - .find(|w| w.get_fullscreen().is_some_and(|s| s == surface)); + .find(|w| w.get_fullscreen_surfaces().any(|f| &f.surface == surface)); if let Some(workspace) = maybe_fullscreen_workspace { - element_geo = Some(workspace.fullscreen_geometry().unwrap()); - let (surface, state, _) = workspace.remove_fullscreen().unwrap(); + let fs = workspace + .get_fullscreen_surfaces() + .find(|f| &f.surface == surface) + .unwrap(); + element_geo = Some(workspace.fullscreen_geometry_for(fs)); + let (surface, state, _) = workspace.remove_fullscreen_surface(surface).unwrap(); self.remap_unfullscreened_window(surface, state, evlh); }; @@ -3902,19 +3905,15 @@ impl Shell { /// Get the window geometry of a keyboard focus target pub fn focused_geometry(&self, target: &KeyboardFocusTarget) -> Option> { match target { - KeyboardFocusTarget::Fullscreen(surface) => { - if let Some(workspace) = surface - .wl_surface() - .and_then(|s| self.workspace_for_surface(&s)) - .and_then(|(handle, _)| self.workspaces.space_for_handle(&handle)) - { + KeyboardFocusTarget::Fullscreen(surface) => surface + .wl_surface() + .and_then(|s| self.workspace_for_surface(&s)) + .and_then(|(handle, _)| self.workspaces.space_for_handle(&handle)) + .map(|workspace| { workspace - .fullscreen_geometry() - .map(|f| f.to_global(workspace.output())) - } else { - None - } - } + .fullscreen_geometry_for_surface(surface) + .to_global(workspace.output()) + }), _ => { if let Some(element) = self.focused_element(target) { self.element_geometry(&element) @@ -4266,9 +4265,8 @@ impl Shell { self.workspaces.sets.values_mut().find_map(|set| { set.workspaces.iter_mut().find_map(|workspace| { let window = workspace - .get_fullscreen() - .cloned() - .into_iter() + .get_fullscreen_surfaces() + .map(|f| f.surface.clone()) .chain(workspace.mapped().map(|m| m.active_window())) .find(|s| s == surface); window.map(|s| (workspace, s)) @@ -4768,15 +4766,16 @@ impl Shell { &mut self, surface: &S, output: Output, - loop_handle: &LoopHandle<'static, State>, + _loop_handle: &LoopHandle<'static, State>, ) -> Option where CosmicSurface: PartialEq, { let mapped = self.element_for_surface(surface).cloned()?; + let seat = self.seats.last_active().clone(); let window; - let old_fullscreen = if let Some((old_output, set)) = self + if let Some((old_output, set)) = self .workspaces .sets .iter_mut() @@ -4823,7 +4822,7 @@ impl Shell { let workspace = self.active_space_mut(&output).unwrap(); workspace.map_fullscreen( &window, - None, + &seat, Some(FullscreenRestoreState::Sticky { output: old_output, state: FloatingRestoreData { @@ -4834,7 +4833,7 @@ impl Shell { }, }), Some(from), - ) + ); } else if let Some(workspace) = self.space_for_mut(&mapped) { if mapped.is_minimized() { // TODO: Rewrite the `MinimizedWindow` to restore to fullscreen @@ -4860,7 +4859,7 @@ impl Shell { workspace.map_fullscreen( &window, - None, + &seat, match state { WorkspaceRestoreData::Floating(floating_state) => { floating_state.map(|state| FullscreenRestoreState::Floating { @@ -4877,15 +4876,11 @@ impl Shell { WorkspaceRestoreData::Fullscreen(_) => unreachable!(), }, Some(from), - ) + ); } else { return None; }; - if let Some((old_fullscreen, restore, _)) = old_fullscreen { - self.remap_unfullscreened_window(old_fullscreen, restore, loop_handle); - } - Some(KeyboardFocusTarget::Fullscreen(window)) } @@ -4900,11 +4895,12 @@ impl Shell { let maybe_workspace = self.workspaces.iter_mut().find_map(|(_, s)| { s.workspaces .iter_mut() - .find(|w| w.get_fullscreen().is_some_and(|f| f == surface)) + .find(|w| w.get_fullscreen_surfaces().any(|f| &f.surface == surface)) }); if let Some(workspace) = maybe_workspace { - let (old_fullscreen, restore, _) = workspace.remove_fullscreen().unwrap(); + let (old_fullscreen, restore, _) = + workspace.remove_fullscreen_surface(surface).unwrap(); toplevel_leave_output(&old_fullscreen, &workspace.output); toplevel_leave_workspace(&old_fullscreen, &workspace.handle); diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index ccf5e4ab..ce5bf624 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -108,7 +108,7 @@ pub struct Workspace { pub floating_layer: FloatingLayout, pub minimized_windows: Vec, pub tiling_enabled: bool, - pub fullscreen: Option, + pub fullscreen_surfaces: Vec, pub pinned: bool, pub id: Option, @@ -383,7 +383,7 @@ impl Workspace { floating_layer, tiling_enabled, minimized_windows: Vec::new(), - fullscreen: None, + fullscreen_surfaces: Vec::new(), pinned: false, id: None, handle, @@ -416,7 +416,7 @@ impl Workspace { floating_layer, tiling_enabled: pinned.tiling_enabled, minimized_windows: Vec::new(), - fullscreen: None, + fullscreen_surfaces: Vec::new(), pinned: true, id: pinned.id.clone(), handle, @@ -454,7 +454,10 @@ impl Workspace { #[profiling::function] pub fn refresh(&mut self) { - self.fullscreen.take_if(|w| !w.alive()); + // seems it removes dead windows + // self.fullscreen.take_if(|w| !w.alive()); + self.fullscreen_surfaces.retain(|w| w.alive()); + self.floating_layer.refresh(); self.tiling_layer.refresh(); } @@ -478,14 +481,15 @@ impl Workspace { self.is_empty() && !self.has_activation_token(xdg_activation_state) && !self.pinned } + /// cleans up any window that is not alive anymore pub fn refresh_focus_stack(&mut self) { for (seat, stack) in self.focus_stack.0.iter_mut() { - let fullscreen = self - .fullscreen - .as_ref() - .filter(|f| f.alive()) - .filter(|f| f.ended_at.is_none()) - .map(|f| &f.surface); + let fullscreen_surfaces: Vec<&CosmicSurface> = self + .fullscreen_surfaces + .iter() + .filter(|f| f.alive() && f.ended_at.is_none()) + .map(|f| &f.surface) + .collect(); // Move grab is treated as focused, so don't change focus to a // window while grab exists. @@ -506,7 +510,7 @@ impl Workspace { .chain(move_mapped.iter()) }; stack.retain(|w| match w { - FocusTarget::Fullscreen(s) => fullscreen.is_some_and(|f| f == s), + FocusTarget::Fullscreen(s) => fullscreen_surfaces.contains(&s), FocusTarget::Window(w) => mapped().any(|m| w == m), }); } @@ -515,15 +519,12 @@ impl Workspace { pub fn animations_going(&self) -> bool { self.tiling_layer.animations_going() || self.floating_layer.animations_going() - || self - .fullscreen - .as_ref() - .is_some_and(|f| f.start_at.is_some() || f.ended_at.is_some()) + || self.fullscreen_surfaces.iter().any(|f| f.is_animating()) || self.dirty.swap(false, Ordering::SeqCst) } pub fn update_animations(&mut self) -> HashMap { - if let Some(f) = self.fullscreen.as_mut() { + for f in self.fullscreen_surfaces.iter_mut() { if let Some(start) = f.start_at.as_ref() { let duration_since = Instant::now().duration_since(*start); if duration_since > FULLSCREEN_ANIMATION_DURATION { @@ -531,16 +532,18 @@ impl Workspace { self.dirty.store(true, Ordering::SeqCst); } } - - if let Some(end) = f.ended_at { - let duration_since = Instant::now().duration_since(end); - if duration_since >= FULLSCREEN_ANIMATION_DURATION { - let _ = self.fullscreen.take(); - self.dirty.store(true, Ordering::SeqCst); - } - } } + self.fullscreen_surfaces.retain(|f| { + if let Some(end) = f.ended_at + && Instant::now().duration_since(end) >= FULLSCREEN_ANIMATION_DURATION + { + self.dirty.store(true, Ordering::SeqCst); + return false; + } + true + }); + let clients = self.tiling_layer.update_animation_state(); self.floating_layer.update_animation_state(); clients @@ -574,7 +577,11 @@ impl Workspace { toplevel_enter_output(&surface, output); } } - if let Some(f) = self.fullscreen.as_ref().filter(|f| f.ended_at.is_none()) { + for f in self + .fullscreen_surfaces + .iter() + .filter(|f| f.ended_at.is_none()) + { toplevel_leave_output(&f.surface, &self.output); toplevel_enter_output(&f.surface, output); } @@ -665,12 +672,13 @@ impl Workspace { where CosmicSurface: PartialEq, { - if self - .fullscreen - .as_ref() - .is_some_and(|f| f.ended_at.is_none() && &f.surface == surface) + if let Some(idx) = self + .fullscreen_surfaces + .iter() + .position(|f| f.ended_at.is_none() && &f.surface == surface) { - let (surface, previous_state, previous_geometry) = self.remove_fullscreen().unwrap(); + let (surface, previous_state, previous_geometry) = + self.remove_fullscreen_at(idx).unwrap(); return Some(( surface, WorkspaceRestoreData::Fullscreen(previous_state.zip(previous_geometry).map( @@ -723,24 +731,28 @@ impl Workspace { Some((mapped.active_window(), layer)) } - pub fn fullscreen_geometry(&self) -> Option> { - self.fullscreen.as_ref().map(|fullscreen| { - let bbox = fullscreen.surface.bbox().as_local(); + pub fn fullscreen_geometry_for_surface( + &self, + surface: &CosmicSurface, + ) -> Rectangle { + let bbox = surface.bbox().as_local(); - let mut full_geo = Rectangle::from_size(self.output.geometry().size.as_local()); - if bbox != full_geo { - if bbox.size.w < full_geo.size.w { - full_geo.loc.x += (full_geo.size.w - bbox.size.w) / 2; - full_geo.size.w = bbox.size.w; - } - if bbox.size.h < full_geo.size.h { - full_geo.loc.y += (full_geo.size.h - bbox.size.h) / 2; - full_geo.size.h = bbox.size.h; - } + let mut full_geo = Rectangle::from_size(self.output.geometry().size.as_local()); + if bbox != full_geo { + if bbox.size.w < full_geo.size.w { + full_geo.loc.x += (full_geo.size.w - bbox.size.w) / 2; + full_geo.size.w = bbox.size.w; } + if bbox.size.h < full_geo.size.h { + full_geo.loc.y += (full_geo.size.h - bbox.size.h) / 2; + full_geo.size.h = bbox.size.h; + } + } - full_geo - }) + full_geo + } + pub fn fullscreen_geometry_for(&self, fullscreen: &FullscreenSurface) -> Rectangle { + self.fullscreen_geometry_for_surface(&fullscreen.surface) } pub fn element_for_surface(&self, surface: &S) -> Option<&CosmicMapped> @@ -780,13 +792,12 @@ impl Workspace { let stack = self.focus_stack.get(seat); let last_focused = stack.last(); - if let Some(fullscreen) = self.fullscreen.as_ref() - && last_focused.is_some_and( - |t| matches!(t, FocusTarget::Fullscreen(f) if f == &fullscreen.surface), - ) - && !fullscreen.is_animating() - { - let geometry = self.fullscreen_geometry().unwrap(); + if let Some(fullscreen) = self.fullscreen_surfaces.iter().find(|f| { + !f.is_animating() + && last_focused + .is_some_and(|t| matches!(t, FocusTarget::Fullscreen(s) if s == &f.surface)) + }) { + let geometry = self.fullscreen_geometry_for(fullscreen); return fullscreen_element_under(fullscreen, geometry); } @@ -795,9 +806,9 @@ impl Workspace { .or_else(|| self.tiling_layer.popup_element_under(location, seat)) .or_else(|| { if last_focused.is_none_or(|t| !matches!(t, FocusTarget::Fullscreen(_))) - && let Some(fullscreen) = self.fullscreen.as_ref() + && let Some(fullscreen) = self.get_fullscreen(seat) { - let geometry = self.fullscreen_geometry().unwrap(); + let geometry = self.fullscreen_geometry_for(fullscreen); return fullscreen_element_under(fullscreen, geometry); } None @@ -830,13 +841,12 @@ impl Workspace { let stack = self.focus_stack.get(seat); let last_focused = stack.last(); - if let Some(fullscreen) = self.fullscreen.as_ref() - && last_focused.is_some_and( - |t| matches!(t, FocusTarget::Fullscreen(f) if f == &fullscreen.surface), - ) - && !fullscreen.is_animating() - { - let geometry = self.fullscreen_geometry().unwrap(); + if let Some(fullscreen) = self.fullscreen_surfaces.iter().find(|fs| { + !fs.is_animating() + && last_focused + .is_some_and(|t| matches!(t, FocusTarget::Fullscreen(f) if f == &fs.surface)) + }) { + let geometry = self.fullscreen_geometry_for(fullscreen); return fullscreen_element_under(fullscreen, geometry); } @@ -845,9 +855,9 @@ impl Workspace { .or_else(|| self.tiling_layer.toplevel_element_under(location, seat)) .or_else(|| { if last_focused.is_none_or(|t| !matches!(t, FocusTarget::Fullscreen(_))) - && let Some(fullscreen) = self.fullscreen.as_ref() + && let Some(fullscreen) = self.get_fullscreen(seat) { - let geometry = self.fullscreen_geometry().unwrap(); + let geometry = self.fullscreen_geometry_for(fullscreen); return fullscreen_element_under(fullscreen, geometry); } None @@ -867,7 +877,7 @@ impl Workspace { let check_fullscreen = |fullscreen: &FullscreenSurface| { if !fullscreen.is_animating() { - let geometry = self.fullscreen_geometry().unwrap(); + let geometry = self.fullscreen_geometry_for(fullscreen); return fullscreen .surface .0 @@ -891,21 +901,16 @@ impl Workspace { let stack = self.focus_stack.get(seat); let last_focused = stack.last(); - self.fullscreen - .as_ref() - .filter(|f| last_focused.is_some_and(|t| t == &f.surface)) + self.fullscreen_surfaces + .iter() + .find(|f| last_focused.is_some_and(|t| t == &f.surface)) .and_then(check_fullscreen) .or_else(|| self.floating_layer.popup_surface_under(location, seat)) .or_else(|| { self.tiling_layer .popup_surface_under(location, overview, seat) }) - .or_else(|| { - self.fullscreen - .as_ref() - .filter(|f| last_focused.is_none_or(|t| t != &f.surface)) - .and_then(check_fullscreen) - }) + .or_else(|| self.get_fullscreen(seat).and_then(check_fullscreen)) .map(|(m, p)| (m, p.to_global(&self.output))) } @@ -922,7 +927,7 @@ impl Workspace { let check_fullscreen = |fullscreen: &FullscreenSurface| { if !fullscreen.is_animating() { - let geometry = self.fullscreen_geometry().unwrap(); + let geometry = self.fullscreen_geometry_for(fullscreen); return fullscreen .surface .focus_under( @@ -940,21 +945,16 @@ impl Workspace { let stack = self.focus_stack.get(seat); let last_focused = stack.last(); - self.fullscreen - .as_ref() - .filter(|f| last_focused.is_some_and(|t| t == &f.surface)) + self.fullscreen_surfaces + .iter() + .find(|f| last_focused.is_some_and(|t| t == &f.surface)) .and_then(check_fullscreen) .or_else(|| self.floating_layer.toplevel_surface_under(location, seat)) .or_else(|| { self.tiling_layer .toplevel_surface_under(location, overview, seat) }) - .or_else(|| { - self.fullscreen - .as_ref() - .filter(|f| last_focused.is_none_or(|t| t != &f.surface)) - .and_then(check_fullscreen) - }) + .or_else(|| self.get_fullscreen(seat).and_then(check_fullscreen)) .map(|(m, p)| (m, p.to_global(&self.output))) } @@ -1026,10 +1026,14 @@ impl Workspace { where CosmicSurface: PartialEq, { - if self.get_fullscreen().is_some_and(|s| s == surface) { - let fullscreen_state = self.fullscreen.clone().unwrap(); + if let Some(idx) = self + .fullscreen_surfaces + .iter() + .position(|f| f.ended_at.is_none() && &f.surface == surface) + { + let fullscreen_state = self.fullscreen_surfaces.get(idx)?.clone(); { - let f = self.fullscreen.as_mut().unwrap(); + let f = self.fullscreen_surfaces.get_mut(idx)?; f.previous_geometry = Some(to); f.ended_at = Some( Instant::now() @@ -1133,16 +1137,20 @@ impl Workspace { )> { match window { MinimizedWindow::Fullscreen { previous, surface } => { - let old_fullscreen = self.remove_fullscreen(); surface.set_minimized(false); - self.fullscreen = Some(FullscreenSurface { + // focus it so it's the top fullscreen window + self.focus_stack + .get_mut(seat) + .append(FocusTarget::Fullscreen(surface.clone())); + self.fullscreen_surfaces.push(FullscreenSurface { surface, previous_state: previous.clone().map(|p| p.previous_state), previous_geometry: previous.map(|p| p.previous_geometry), start_at: None, ended_at: None, }); - old_fullscreen + self.dirty.store(true, Ordering::SeqCst); + None } MinimizedWindow::Floating { window, previous } => { let current_output_size = self.output.geometry().size.as_logical(); @@ -1230,13 +1238,7 @@ impl Workspace { seat: impl Into>>, restore: Option, previous_geometry: Option>, - ) -> Option<( - CosmicSurface, - Option, - Option>, - )> { - let res = self.remove_fullscreen(); - + ) { window.set_fullscreen(true); window.set_geometry(self.output.geometry(), 0); window.send_configure(); @@ -1249,96 +1251,137 @@ impl Workspace { self.focus_stack.get_mut(seat).append(window.clone()); } - self.fullscreen = Some(FullscreenSurface { + self.dirty.store(true, Ordering::SeqCst); + self.fullscreen_surfaces.push(FullscreenSurface { surface: window.clone(), previous_state: restore, previous_geometry, start_at: Some(Instant::now()), ended_at: None, }); - - res } #[must_use] - pub fn take_fullscreen( + pub fn take_fullscreen( &mut self, + surface: &S, + ) -> Option<( + CosmicSurface, + Option, + Option>, + )> + where + CosmicSurface: PartialEq, + { + let idx = self + .fullscreen_surfaces + .iter() + .position(|f| f.ended_at.is_none() && &f.surface == surface)?; + let fs = self.fullscreen_surfaces.remove(idx); + + for focus_stack in self.focus_stack.0.values_mut() { + focus_stack.retain(|t| t != &fs.surface); + } + + Some((fs.surface, fs.previous_state, fs.previous_geometry)) + } + + #[must_use] + pub fn remove_fullscreen_at( + &mut self, + idx: usize, ) -> Option<( CosmicSurface, Option, Option>, )> { - let surface = self.fullscreen.take_if(|s| s.ended_at.is_none())?; + // if it doesn't exist we move on. + let surface = self.fullscreen_surfaces.get_mut(idx)?; + // if already being removed, do nothing + if surface.ended_at.is_some() { + return None; + } + + if surface.surface.alive() { + surface.surface.output_leave(&self.output); + surface.surface.set_fullscreen(false); + if let Some(previous_geometry) = surface.previous_geometry.as_ref() { + surface + .surface + .set_geometry(previous_geometry.to_global(&self.output), 0); + } + surface.surface.send_configure(); + } for focus_stack in self.focus_stack.0.values_mut() { focus_stack.retain(|t| t != &surface.surface); } + surface.ended_at = Some( + Instant::now() + - (FULLSCREEN_ANIMATION_DURATION + - surface + .start_at + .take() + .map(|earlier| { + Instant::now() + .duration_since(earlier) + .min(FULLSCREEN_ANIMATION_DURATION) + }) + .unwrap_or(FULLSCREEN_ANIMATION_DURATION)), + ); + Some(( - surface.surface, - surface.previous_state, + surface.surface.clone(), + surface.previous_state.clone(), surface.previous_geometry, )) } #[must_use] - pub fn remove_fullscreen( + pub fn remove_fullscreen_surface( &mut self, + surface: &S, ) -> Option<( CosmicSurface, Option, Option>, - )> { - if let Some(surface) = self.fullscreen.as_mut() { - if surface.ended_at.is_some() { - return None; - } - - if surface.surface.alive() { - surface.surface.output_leave(&self.output); - surface.surface.set_fullscreen(false); - if let Some(previous_geometry) = surface.previous_geometry.as_ref() { - surface - .surface - .set_geometry(previous_geometry.to_global(&self.output), 0); - } - surface.surface.send_configure(); - } - - for focus_stack in self.focus_stack.0.values_mut() { - focus_stack.retain(|t| t != &surface.surface); - } - - surface.ended_at = Some( - Instant::now() - - (FULLSCREEN_ANIMATION_DURATION - - surface - .start_at - .take() - .map(|earlier| { - Instant::now() - .duration_since(earlier) - .min(FULLSCREEN_ANIMATION_DURATION) - }) - .unwrap_or(FULLSCREEN_ANIMATION_DURATION)), - ); - - Some(( - surface.surface.clone(), - surface.previous_state.clone(), - surface.previous_geometry, - )) - } else { - None - } + )> + where + CosmicSurface: PartialEq, + { + let idx = self + .fullscreen_surfaces + .iter() + .position(|f| f.ended_at.is_none() && &f.surface == surface)?; + self.remove_fullscreen_at(idx) } - pub fn get_fullscreen(&self) -> Option<&CosmicSurface> { - self.fullscreen - .as_ref() - .filter(|f| f.alive()) - .filter(|f| f.ended_at.is_none()) - .map(|f| &f.surface) + pub fn get_fullscreen(&self, seat: &Seat) -> Option<&FullscreenSurface> { + let stack = self.focus_stack.get(seat); + stack + .iter() + .find_map(|t| { + if let FocusTarget::Fullscreen(s) = t { + self.fullscreen_surfaces + .iter() + .find(|f| f.alive() && f.ended_at.is_none() && &f.surface == s) + } else { + None + } + }) + .or_else(|| { + self.fullscreen_surfaces + .iter() + .rev() + .find(|f| f.alive() && f.ended_at.is_none()) + }) + } + + pub fn get_fullscreen_surfaces(&self) -> impl Iterator { + self.fullscreen_surfaces + .iter() + .filter(|f| f.alive() && f.ended_at.is_none()) } pub fn resize( @@ -1465,14 +1508,18 @@ impl Workspace { self.floating_layer.mapped().count() + self.tiling_layer.mapped().count() + self.minimized_windows.len() - + if self.fullscreen.is_some() { 1 } else { 0 } + + self + .fullscreen_surfaces + .iter() + .filter(|f| f.ended_at.is_none()) + .count() } pub fn is_empty(&self) -> bool { self.floating_layer.mapped().next().is_none() && self.tiling_layer.mapped().next().is_none() && self.minimized_windows.is_empty() - && self.fullscreen.is_none() + && self.fullscreen_surfaces.is_empty() } pub fn is_floating(&self, surface: &S) -> bool @@ -1579,8 +1626,11 @@ impl Workspace { }; let focused = self.focus_stack.get(last_active_seat).last().cloned(); - let mut fullscreen_elements = if let Some(fullscreen) = self.fullscreen.as_ref() { - let fullscreen_geo = self.fullscreen_geometry().unwrap(); + let render_fullscreen = |fullscreen: &FullscreenSurface, + renderer: &mut R, + output_scale: f64| + -> Vec> { + let fullscreen_geo = self.fullscreen_geometry_for(fullscreen); let previous_geo = fullscreen .previous_geometry .as_ref() @@ -1650,20 +1700,35 @@ impl Workspace { .into_iter() .map(animation_rescale) .collect::>() - } else { - Vec::new() }; + let top_fullscreen = self.get_fullscreen(last_active_seat); + + let mut fullscreen_elements: Vec> = Vec::new(); + if let Some(fs) = top_fullscreen { + fullscreen_elements.extend(render_fullscreen(fs, renderer, output_scale)); + } + // Also render any animating (entering/exiting) fullscreens + for fs in self.fullscreen_surfaces.iter().filter(|f| f.is_animating()) { + if top_fullscreen.is_none_or(|top| top.surface != fs.surface) { + fullscreen_elements.extend(render_fullscreen(fs, renderer, output_scale)); + }; + } + if matches!(focused, Some(FocusTarget::Fullscreen(_))) { elements.append(&mut fullscreen_elements); } + let any_fullscreen_animating = self + .fullscreen_surfaces + .iter() + .any(|f| f.start_at.is_some() || f.ended_at.is_some()); if !matches!(focused, Some(FocusTarget::Fullscreen(_))) + || any_fullscreen_animating || self - .fullscreen - .as_ref() - .map(|f| f.start_at.is_some() || f.ended_at.is_some()) - .unwrap_or(true) + .fullscreen_surfaces + .iter() + .all(|f| !f.alive() || f.ended_at.is_some()) { // floating surfaces let alpha = match &overview.0 { @@ -1780,8 +1845,12 @@ impl Workspace { layer_map.non_exclusive_zone().as_local() }; - if let Some(fullscreen) = self.fullscreen.as_ref() { - let fullscreen_geo = self.fullscreen_geometry().unwrap(); + // Render popups for the top (most recently focused) fullscreen + let focus_stack = self.focus_stack.get(last_active_seat); + let top_fullscreen = self.get_fullscreen(last_active_seat); + + if let Some(fullscreen) = top_fullscreen { + let fullscreen_geo = self.fullscreen_geometry_for(fullscreen); let previous_geo = fullscreen .previous_geometry .as_ref() @@ -1838,13 +1907,16 @@ impl Workspace { ); } - let focus_stack = self.focus_stack.get(last_active_seat); + let any_fullscreen_animating = self + .fullscreen_surfaces + .iter() + .any(|f| f.start_at.is_some() || f.ended_at.is_some()); if !matches!(focus_stack.last(), Some(FocusTarget::Fullscreen(_))) + || any_fullscreen_animating || self - .fullscreen - .as_ref() - .map(|f| f.start_at.is_some() || f.ended_at.is_some()) - .unwrap_or(true) + .fullscreen_surfaces + .iter() + .all(|f| !f.alive() || f.ended_at.is_some()) { // floating surfaces let alpha = match &overview.0 { diff --git a/src/state.rs b/src/state.rs index 88298a94..935261ac 100644 --- a/src/state.rs +++ b/src/state.rs @@ -996,8 +996,8 @@ impl Common { // normal windows for space in shell.workspaces.spaces() { - if let Some(window) = space.get_fullscreen() { - window.with_surfaces(processor); + if let Some(fs) = space.get_fullscreen(shell.seats.last_active()) { + fs.surface.with_surfaces(processor); } space.mapped().for_each(|mapped| { for (window, _) in mapped.windows() { @@ -1162,15 +1162,16 @@ impl Common { }); if let Some(active) = shell.active_space(output) { - if let Some(window) = active.get_fullscreen() - && let Some(feedback) = window + if let Some(fs) = active.get_fullscreen(shell.seats.last_active()) + && let Some(feedback) = fs + .surface .wl_surface() .and_then(|wl_surface| { advertised_node_for_surface(&wl_surface, &self.display_handle) }) .and_then(&mut dmabuf_feedback) { - window.send_dmabuf_feedback( + fs.surface.send_dmabuf_feedback( output, &feedback, render_element_states, @@ -1356,8 +1357,9 @@ impl Common { }); if let Some(active) = shell.active_space(output) { - if let Some(window) = active.get_fullscreen() { - window.send_frame(output, time, throttle(window), should_send); + if let Some(fs) = active.get_fullscreen(shell.seats.last_active()) { + fs.surface + .send_frame(output, time, throttle(&fs.surface), should_send); } active.mapped().for_each(|mapped| { for (window, _) in mapped.windows() { @@ -1377,9 +1379,9 @@ impl Common { .spaces_for_output(output) .filter(|w| w.handle != active.handle) { - if let Some(window) = space.get_fullscreen() { - let throttle = min(throttle(space), throttle(window)); - window.send_frame(output, time, throttle, |_, _| None); + if let Some(fs) = space.get_fullscreen(shell.seats.last_active()) { + let throttle = min(throttle(space), throttle(&fs.surface)); + fs.surface.send_frame(output, time, throttle, |_, _| None); } space.mapped().for_each(|mapped| { for (window, _) in mapped.windows() { diff --git a/src/wayland/handlers/dmabuf.rs b/src/wayland/handlers/dmabuf.rs index 093f88da..4d9732bb 100644 --- a/src/wayland/handlers/dmabuf.rs +++ b/src/wayland/handlers/dmabuf.rs @@ -51,9 +51,11 @@ impl DmabufHandler for State { let is_fullscreen = shell .workspaces .space_for_handle(&handle)? - .fullscreen - .as_ref() - .is_some_and(|f| f.surface.has_surface(surface, WindowSurfaceType::all())); + .fullscreen_surfaces + .iter() + .any(|f| { + f.ended_at.is_none() && f.surface.has_surface(surface, WindowSurfaceType::all()) + }); let node = kms .drm_devices diff --git a/src/wayland/handlers/toplevel_management.rs b/src/wayland/handlers/toplevel_management.rs index 01410bb5..3da39952 100644 --- a/src/wayland/handlers/toplevel_management.rs +++ b/src/wayland/handlers/toplevel_management.rs @@ -42,7 +42,7 @@ impl ToplevelManagementHandler for State { .spaces_for_output(output) .enumerate() .find(|(_, w)| { - w.get_fullscreen().is_some_and(|f| f == window) + w.get_fullscreen_surfaces().any(|f| &f.surface == window) || w.mapped() .flat_map(|m| m.windows().map(|(s, _)| s)) .any(|w| &w == window) diff --git a/src/wayland/handlers/xdg_activation.rs b/src/wayland/handlers/xdg_activation.rs index b9674e38..359f654d 100644 --- a/src/wayland/handlers/xdg_activation.rs +++ b/src/wayland/handlers/xdg_activation.rs @@ -219,8 +219,8 @@ impl State { .workspaces .space_for_handle(&workspace) .unwrap() - .get_fullscreen() - .cloned() + .get_fullscreen(&seat) + .map(|f| f.surface.clone()) .map(KeyboardFocusTarget::Fullscreen) else { return; @@ -232,8 +232,8 @@ impl State { if let Some(surface) = shell .workspaces .space_for_handle(&workspace) - .and_then(|w| w.get_fullscreen()) - .cloned() + .and_then(|w| w.get_fullscreen(&seat)) + .map(|f| f.surface.clone()) { shell.append_focus_stack(surface, &seat) } diff --git a/src/wayland/handlers/xdg_shell/popup.rs b/src/wayland/handlers/xdg_shell/popup.rs index 965e8908..a3ee2e31 100644 --- a/src/wayland/handlers/xdg_shell/popup.rs +++ b/src/wayland/handlers/xdg_shell/popup.rs @@ -68,7 +68,7 @@ impl Shell { unconstrain_xdg_popup(surface, window_loc, output.geometry()); } } else if let Some(output) = self.workspaces.spaces().find_map(|w| { - w.fullscreen.as_ref().and_then(|f| { + w.fullscreen_surfaces.iter().find_map(|f| { (f.surface.wl_surface().as_deref() == Some(&parent)).then_some(w.output()) }) }) { diff --git a/src/xwayland.rs b/src/xwayland.rs index 4c2f3b55..eb3e1570 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -575,16 +575,15 @@ impl Common { .filter(|(i, _)| *i != set.active), ) .flat_map(|(_, workspace)| { + let focus_last = + workspace.focus_stack.get(seat).last().cloned(); workspace - .get_fullscreen() + .get_fullscreen_surfaces() .filter(|f| { - workspace - .focus_stack - .get(seat) - .last() - .is_some_and(|t| &t == f) + focus_last.as_ref().is_some_and(|t| t == &f.surface) }) - .cloned() + .map(|f| f.surface.clone()) + .collect::>() .into_iter() .chain(workspace.mapped().flat_map(|mapped| { let active = mapped.active_window(); @@ -603,15 +602,14 @@ impl Common { })) .chain( workspace - .get_fullscreen() + .get_fullscreen_surfaces() .filter(|f| { - workspace - .focus_stack - .get(seat) - .last() - .is_none_or(|t| &t != f) + focus_last + .as_ref() + .is_none_or(|t| t != &f.surface) }) - .cloned() + .map(|f| f.surface.clone()) + .collect::>() .into_iter(), ) .chain( From 56f84fba2dc0ff7de191e48e56f29d92ba50e668 Mon Sep 17 00:00:00 2001 From: Hojjat Date: Mon, 25 May 2026 12:04:41 -0600 Subject: [PATCH 14/64] fix: follow the focus after alt+tab to another output --- src/wayland/handlers/toplevel_management.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/wayland/handlers/toplevel_management.rs b/src/wayland/handlers/toplevel_management.rs index 3da39952..2b1232d5 100644 --- a/src/wayland/handlers/toplevel_management.rs +++ b/src/wayland/handlers/toplevel_management.rs @@ -111,10 +111,8 @@ impl ToplevelManagementHandler for State { std::mem::drop(shell); // move pointer to window if it’s on a different monitor/output - if seat.active_output() != *output - && self.common.config.cosmic_conf.cursor_follows_focus - && let Some(new_pos) = new_pos - { + let switching_output = seat.active_output() != *output; + if switching_output && let Some(new_pos) = new_pos { seat.set_active_output(output); if let Some(ptr) = seat.get_pointer() { let serial = SERIAL_COUNTER.next_serial(); From 2f7c34f29ae8d1d5b7d147bdb69ae443bd630e98 Mon Sep 17 00:00:00 2001 From: Robin Nehls Date: Fri, 22 May 2026 17:06:49 +0200 Subject: [PATCH 15/64] feat: add config option for xdg activation behavior --- cosmic-comp-config/src/lib.rs | 10 ++++++ src/config/mod.rs | 10 ++++-- src/wayland/handlers/xdg_activation.rs | 44 +++++++++++++++++++++++--- 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/cosmic-comp-config/src/lib.rs b/cosmic-comp-config/src/lib.rs index 8c4d9588..e5e73883 100644 --- a/cosmic-comp-config/src/lib.rs +++ b/cosmic-comp-config/src/lib.rs @@ -101,6 +101,7 @@ pub struct CosmicCompConfig { pub appearance_settings: AppearanceConfig, /// Hide the cursor after this many seconds of pointer inactivity (None disables) pub cursor_hide_timeout: Option, + pub activation_policy: ActivationPolicy, } impl Default for CosmicCompConfig { @@ -138,6 +139,7 @@ impl Default for CosmicCompConfig { accessibility_zoom: ZoomConfig::default(), appearance_settings: AppearanceConfig::default(), cursor_hide_timeout: None, + activation_policy: ActivationPolicy::default(), } } } @@ -227,6 +229,14 @@ pub enum EavesdroppingKeyboardMode { All, } +#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialEq, Eq)] +pub enum ActivationPolicy { + #[default] + Focus, + FocusIfActiveWorkspace, + Urgent, +} + #[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialEq, Eq)] #[serde(rename_all = "lowercase")] pub enum XwaylandDescaling { diff --git a/src/config/mod.rs b/src/config/mod.rs index d75eed4e..1d9b9c92 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -45,8 +45,8 @@ mod types; use cosmic::config::CosmicTk; pub use cosmic_comp_config::EdidProduct; use cosmic_comp_config::{ - AppearanceConfig, CosmicCompConfig, KeyboardConfig, TileBehavior, XkbConfig, XwaylandDescaling, - XwaylandEavesdropping, ZoomConfig, + ActivationPolicy, AppearanceConfig, CosmicCompConfig, KeyboardConfig, TileBehavior, XkbConfig, + XwaylandDescaling, XwaylandEavesdropping, ZoomConfig, input::{DeviceState as InputDeviceState, InputConfig, TouchpadOverride}, output::comp::{ OutputConfig, OutputInfo, OutputState, OutputsConfig, TransformDef, load_outputs, @@ -966,6 +966,12 @@ fn config_changed(config: cosmic_config::Config, keys: Vec, state: &mut } } } + "activation_policy" => { + let new = get_config::(&config, "activation_policy"); + if new != state.common.config.cosmic_conf.activation_policy { + state.common.config.cosmic_conf.activation_policy = new; + } + } _ => {} } } diff --git a/src/wayland/handlers/xdg_activation.rs b/src/wayland/handlers/xdg_activation.rs index 359f654d..03c3c629 100644 --- a/src/wayland/handlers/xdg_activation.rs +++ b/src/wayland/handlers/xdg_activation.rs @@ -5,6 +5,7 @@ use crate::{ state::State, wayland::protocols::workspace::{State as WState, WorkspaceHandle}, }; +use cosmic_comp_config::ActivationPolicy; use smithay::{ input::Seat, reexports::wayland_server::protocol::wl_surface::WlSurface, @@ -115,10 +116,45 @@ impl XdgActivationHandler for State { } } ActivationContext::Workspace(_) => { - self.activate_surface( - &surface, - Some((ActivationKey::Wayland(surface.clone()), *context)), - ); + match self.common.config.cosmic_conf.activation_policy { + ActivationPolicy::Focus => { + self.activate_surface( + &surface, + Some((ActivationKey::Wayland(surface.clone()), *context)), + ); + } + ActivationPolicy::FocusIfActiveWorkspace => { + let shell = self.common.shell.write(); + + let Some((target_workspace, _)) = shell.workspace_for_surface(&surface) + else { + // surface not found, maybe log warning? + return; + }; + + let seat = shell.seats.last_active().clone(); + let current_output = seat.active_output(); + let current_workspace = shell.active_space(¤t_output).unwrap().handle; + + if target_workspace == current_workspace { + std::mem::drop(shell); + self.activate_surface( + &surface, + Some((ActivationKey::Wayland(surface.clone()), *context)), + ); + } else { + let mut workspace_guard = self.common.workspace_state.update(); + workspace_guard.add_workspace_state(&target_workspace, WState::Urgent); + } + } + ActivationPolicy::Urgent => { + let shell = self.common.shell.write(); + if let Some((workspace, _output)) = shell.workspace_for_surface(&surface) { + let mut workspace_guard = self.common.workspace_state.update(); + workspace_guard.add_workspace_state(&workspace, WState::Urgent); + } + } + } } } } From 3c834e9c85d07103317fdc93d71590bcf376dbf3 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Fri, 13 Feb 2026 12:48:48 -0800 Subject: [PATCH 16/64] dbus: Use `FutureSource` instead of a channel --- Cargo.lock | 1 + Cargo.toml | 2 +- src/dbus/mod.rs | 41 ++++++++++++----------------------------- src/state.rs | 2 +- 4 files changed, 15 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 029590c1..0af5da19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -544,6 +544,7 @@ checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" dependencies = [ "async-task", "bitflags 2.11.0", + "futures-core", "polling", "rustix 1.1.4", "slab", diff --git a/Cargo.toml b/Cargo.toml index 9e74d6ea..ae7f67eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ members = ["cosmic-comp-config"] [dependencies] anyhow = { version = "1.0.102", features = ["backtrace"] } bitflags = "2.11.0" -calloop = { version = "0.14.4", features = ["executor"] } +calloop = { version = "0.14.4", features = ["executor", "stream"] } cosmic-comp-config = { path = "cosmic-comp-config", features = [ "libdisplay-info", "output", diff --git a/src/dbus/mod.rs b/src/dbus/mod.rs index dcb7f257..f0f30d6d 100644 --- a/src/dbus/mod.rs +++ b/src/dbus/mod.rs @@ -3,10 +3,9 @@ use crate::{ utils::prelude::OutputExt, }; use anyhow::{Context, Result}; -use calloop::{InsertError, LoopHandle, RegistrationToken}; +use calloop::{InsertError, LoopHandle, RegistrationToken, stream::StreamSource}; use cosmic_comp_config::output::comp::OutputState; -use futures_executor::{ThreadPool, block_on}; -use futures_util::stream::StreamExt; +use futures_executor::block_on; use std::collections::HashMap; use tracing::{error, warn}; use zbus::blocking::{Connection, fdo::DBusProxy}; @@ -17,19 +16,15 @@ pub mod logind; mod name_owners; mod power; -pub fn init( - evlh: &LoopHandle<'static, State>, - executor: &ThreadPool, -) -> Result> { +pub fn init(evlh: &LoopHandle<'static, State>) -> Result> { let mut tokens = Vec::new(); match block_on(power::init()) { Ok(power_daemon) => { - let (tx, rx) = calloop::channel::channel(); - - let token = evlh - .insert_source(rx, |event, _, state| match event { - calloop::channel::Event::Msg(_) => { + if let Ok(stream) = block_on(power_daemon.receive_hot_plug_detect()) { + let source = StreamSource::new(stream).unwrap(); + let token = evlh + .insert_source(source, |_, _, state| { let nodes = match &mut state.backend { BackendData::Kms(kms) => { kms.drm_devices.keys().cloned().collect::>() @@ -56,24 +51,12 @@ pub fn init( } } } - } - calloop::channel::Event::Closed => (), - }) - .map_err(|InsertError { error, .. }| error) - .with_context(|| "Failed to add channel to event_loop")?; + }) + .map_err(|InsertError { error, .. }| error) + .with_context(|| "Failed to add channel to event_loop")?; - // start helper thread - executor.spawn_ok(async move { - if let Ok(mut msg_iter) = power_daemon.receive_hot_plug_detect().await { - while let Some(msg) = msg_iter.next().await { - if tx.send(msg).is_err() { - break; - } - } - } - }); - - tokens.push(token); + tokens.push(token); + } } Err(err) => { tracing::info!(?err, "Failed to connect to com.system76.PowerDaemon"); diff --git a/src/state.rs b/src/state.rs index 935261ac..af0d1e2e 100644 --- a/src/state.rs +++ b/src/state.rs @@ -729,7 +729,7 @@ impl State { let async_executor = ThreadPool::builder().pool_size(1).create().unwrap(); - if let Err(err) = crate::dbus::init(&handle, &async_executor) { + if let Err(err) = crate::dbus::init(&handle) { tracing::warn!(?err, "Failed to initialize dbus handlers"); } From 571565c28ee42da0a2f2b67633ea0133f5a5e744 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Fri, 13 Feb 2026 12:57:05 -0800 Subject: [PATCH 17/64] dbus: Use `calloop` async executor New version of callop has a fix for this, so we're no longer blocked from using it. --- src/dbus/a11y_keyboard_monitor.rs | 11 +++++------ src/dbus/name_owners.rs | 14 +++++++++----- src/state.rs | 6 +++--- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/dbus/a11y_keyboard_monitor.rs b/src/dbus/a11y_keyboard_monitor.rs index c40be081..d09fdac9 100644 --- a/src/dbus/a11y_keyboard_monitor.rs +++ b/src/dbus/a11y_keyboard_monitor.rs @@ -1,6 +1,5 @@ // https://gitlab.gnome.org/GNOME/mutter/-/blob/main/data/dbus-interfaces/org.freedesktop.a11y.xml -use futures_executor::ThreadPool; use smithay::{ backend::input::KeyState, input::keyboard::{KeysymHandle, ModifiersState}, @@ -72,7 +71,7 @@ impl Clients { #[derive(Debug)] pub struct A11yKeyboardMonitorState { - executor: ThreadPool, + executor: calloop::futures::Scheduler<()>, clients: Arc>, active_virtual_mods: HashSet, conn: Arc>, @@ -80,7 +79,7 @@ pub struct A11yKeyboardMonitorState { } impl A11yKeyboardMonitorState { - pub fn new(executor: &ThreadPool) -> Self { + pub fn new(executor: &calloop::futures::Scheduler<()>) -> Self { let clients = Arc::new(Mutex::new(Clients::default())); let clients_clone = clients.clone(); let conn_cell = Arc::new(OnceLock::new()); @@ -88,7 +87,7 @@ impl A11yKeyboardMonitorState { let name_owners_cell = Arc::new(OnceLock::new()); let name_owners_cell_clone = name_owners_cell.clone(); let executor_clone = executor.clone(); - executor.spawn_ok(async move { + let _ = executor.schedule(async move { match serve(clients_clone, &executor_clone).await { Ok((conn, name_owners)) => { conn_cell_clone.set(conn).unwrap(); @@ -186,7 +185,7 @@ impl A11yKeyboardMonitorState { unichar, keysym.raw_code().raw() as u16, ); - self.executor.spawn_ok(async { + let _ = self.executor.schedule(async { let _ = future.await; }); } @@ -314,7 +313,7 @@ impl KeyboardMonitor { async fn serve( clients: Arc>, - executor: &ThreadPool, + executor: &calloop::futures::Scheduler<()>, ) -> zbus::Result<(zbus::Connection, NameOwners)> { let conn = zbus::Connection::session().await?; let name_owners = NameOwners::new(&conn, executor).await?; diff --git a/src/dbus/name_owners.rs b/src/dbus/name_owners.rs index fe3591b5..a2715c73 100644 --- a/src/dbus/name_owners.rs +++ b/src/dbus/name_owners.rs @@ -2,9 +2,10 @@ //! //! Compare to Mutter's `MetaDbusAccessChecker` -use futures_executor::ThreadPool; -use futures_util::stream::FusedStream; -use futures_util::{StreamExt, stream::FuturesUnordered}; +use futures_util::{ + StreamExt, + stream::{FusedStream, FuturesUnordered}, +}; use std::{ collections::{HashMap, HashSet}, future::{Future, poll_fn}, @@ -94,7 +95,10 @@ fn update_task(inner: Weak>) -> impl Future { pub struct NameOwners(Arc>); impl NameOwners { - pub async fn new(connection: &zbus::Connection, executor: &ThreadPool) -> zbus::Result { + pub async fn new( + connection: &zbus::Connection, + executor: &calloop::futures::Scheduler<()>, + ) -> zbus::Result { let dbus = fdo::DBusProxy::new(connection).await?; let stream = dbus.receive_name_owner_changed().await?; @@ -126,7 +130,7 @@ impl NameOwners { })); if enforce { - executor.spawn_ok(update_task(Arc::downgrade(&inner))); + let _ = executor.schedule(update_task(Arc::downgrade(&inner))); } Ok(NameOwners(inner)) diff --git a/src/state.rs b/src/state.rs index af0d1e2e..cfd38eda 100644 --- a/src/state.rs +++ b/src/state.rs @@ -32,7 +32,6 @@ use crate::{ use anyhow::Context; use calloop::RegistrationToken; use cosmic_comp_config::output::comp::{OutputConfig, OutputState}; -use futures_executor::ThreadPool; use i18n_embed::{ DesktopLanguageRequester, fluent::{FluentLanguageLoader, fluent_language_loader}, @@ -234,7 +233,7 @@ pub struct Common { pub display_handle: DisplayHandle, pub event_loop_handle: LoopHandle<'static, State>, pub event_loop_signal: LoopSignal, - pub async_executor: ThreadPool, + pub async_executor: calloop::futures::Scheduler<()>, pub popups: PopupManager, pub shell: Arc>, @@ -727,7 +726,8 @@ impl State { ); let workspace_state = WorkspaceState::new(dh, client_not_sandboxed); - let async_executor = ThreadPool::builder().pool_size(1).create().unwrap(); + let (source, async_executor) = calloop::futures::executor().unwrap(); + handle.insert_source(source, |_, _, _| {}).unwrap(); if let Err(err) = crate::dbus::init(&handle) { tracing::warn!(?err, "Failed to initialize dbus handlers"); From c8d9ff12154d892617eec7fa301a2510b969d88a Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Fri, 13 Feb 2026 12:59:26 -0800 Subject: [PATCH 18/64] dbus: Remove unused return value from `dbus::init` If we don't use this anyway, this can make things simpler. --- src/dbus/mod.rs | 64 ++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/src/dbus/mod.rs b/src/dbus/mod.rs index f0f30d6d..475eaed0 100644 --- a/src/dbus/mod.rs +++ b/src/dbus/mod.rs @@ -3,7 +3,7 @@ use crate::{ utils::prelude::OutputExt, }; use anyhow::{Context, Result}; -use calloop::{InsertError, LoopHandle, RegistrationToken, stream::StreamSource}; +use calloop::{InsertError, LoopHandle, stream::StreamSource}; use cosmic_comp_config::output::comp::OutputState; use futures_executor::block_on; use std::collections::HashMap; @@ -16,54 +16,48 @@ pub mod logind; mod name_owners; mod power; -pub fn init(evlh: &LoopHandle<'static, State>) -> Result> { - let mut tokens = Vec::new(); - +pub fn init(evlh: &LoopHandle<'static, State>) -> Result<()> { match block_on(power::init()) { Ok(power_daemon) => { if let Ok(stream) = block_on(power_daemon.receive_hot_plug_detect()) { let source = StreamSource::new(stream).unwrap(); - let token = evlh - .insert_source(source, |_, _, state| { - let nodes = match &mut state.backend { - BackendData::Kms(kms) => { - kms.drm_devices.keys().cloned().collect::>() - } - _ => Vec::new(), - }; - let mut added = Vec::new(); - for node in nodes { - match state.device_changed(node.dev_id()) { - Ok(outputs) => added.extend(outputs), - Err(err) => { - tracing::error!(?err, "Failed to update drm device {}.", node) - } + evlh.insert_source(source, |_, _, state| { + let nodes = match &mut state.backend { + BackendData::Kms(kms) => { + kms.drm_devices.keys().cloned().collect::>() + } + _ => Vec::new(), + }; + let mut added = Vec::new(); + for node in nodes { + match state.device_changed(node.dev_id()) { + Ok(outputs) => added.extend(outputs), + Err(err) => { + tracing::error!(?err, "Failed to update drm device {}.", node) } } - if let Err(err) = state.refresh_output_config() { - warn!("Unable to load output config: {}", err); - if !added.is_empty() { - for output in added { - output.config_mut().enabled = OutputState::Disabled; - } - if let Err(err) = state.refresh_output_config() { - error!("Unrecoverable config error: {}", err); - } + } + if let Err(err) = state.refresh_output_config() { + warn!("Unable to load output config: {}", err); + if !added.is_empty() { + for output in added { + output.config_mut().enabled = OutputState::Disabled; + } + if let Err(err) = state.refresh_output_config() { + error!("Unrecoverable config error: {}", err); } } - }) - .map_err(|InsertError { error, .. }| error) - .with_context(|| "Failed to add channel to event_loop")?; - - tokens.push(token); + } + }) + .map_err(|InsertError { error, .. }| error) + .with_context(|| "Failed to add channel to event_loop")?; } } Err(err) => { tracing::info!(?err, "Failed to connect to com.system76.PowerDaemon"); } }; - - Ok(tokens) + Ok(()) } /// Updated the D-Bus activation environment with `WAYLAND_DISPLAY` and From 4eaaf4f55cd7976a38c71e66e4fdb8f9c5579007 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 5 Mar 2026 15:56:06 -0800 Subject: [PATCH 19/64] dbus: Refactor to share DBus connections We should avoid creating more than one session connection or more than one system connection. We should also ideally avoid blocking the main thread. For now this still uses blocking in a couple places, but wrapping async code (which is how `zbus::blocking` is implemented anyway). This moves the `NameOwners` creation out of `A11yKeyboardMonitorState`, so it can be shared with other things. We will likely want that for https://github.com/pop-os/cosmic-comp/pull/465 to define a secured protocol to pass an fd to the portal, and potentially any other sensitive DBus protocols implemented by the compositor. --- Cargo.lock | 8 +- Cargo.toml | 4 +- src/dbus/a11y_keyboard_monitor.rs | 82 +++++++------------ src/dbus/logind.rs | 40 +++++---- src/dbus/mod.rs | 131 +++++++++++++++++++++++++----- src/dbus/power.rs | 5 +- src/input/mod.rs | 115 ++++++++++++-------------- src/shell/mod.rs | 4 +- src/state.rs | 22 ++--- 9 files changed, 238 insertions(+), 173 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0af5da19..a94f5810 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -242,6 +242,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-once-cell" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" + [[package]] name = "async-process" version = "2.5.0" @@ -829,6 +835,7 @@ name = "cosmic-comp" version = "1.0.0" dependencies = [ "anyhow", + "async-once-cell", "bitflags 2.11.0", "calloop 0.14.4", "cgmath", @@ -867,7 +874,6 @@ dependencies = [ "sanitize-filename", "serde", "serde_json", - "smallvec", "smithay", "smithay-egui", "thiserror 2.0.18", diff --git a/Cargo.toml b/Cargo.toml index ae7f67eb..e548ad83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,10 +80,10 @@ rand = "0.10" clap_lex = "1.0" parking_lot = "0.12.5" logind-zbus = { version = "5.3.2", optional = true } -futures-executor = { version = "0.3.32", features = ["thread-pool"] } +futures-executor = { version = "0.3.32" } futures-util = "0.3.32" cgmath = "0.18.0" -smallvec = "1.15.1" +async-once-cell = "0.5.4" [dependencies.id_tree] branch = "feature/copy_clone" diff --git a/src/dbus/a11y_keyboard_monitor.rs b/src/dbus/a11y_keyboard_monitor.rs index d09fdac9..cf66a54b 100644 --- a/src/dbus/a11y_keyboard_monitor.rs +++ b/src/dbus/a11y_keyboard_monitor.rs @@ -6,7 +6,7 @@ use smithay::{ }; use std::{ collections::{HashMap, HashSet}, - sync::{Arc, Mutex, OnceLock}, + sync::{Arc, Mutex}, }; use tracing::debug; use xkbcommon::xkb::Keysym; @@ -74,37 +74,34 @@ pub struct A11yKeyboardMonitorState { executor: calloop::futures::Scheduler<()>, clients: Arc>, active_virtual_mods: HashSet, - conn: Arc>, - name_owners: Arc>, + conn: zbus::Connection, + name_owners: NameOwners, } impl A11yKeyboardMonitorState { - pub fn new(executor: &calloop::futures::Scheduler<()>) -> Self { + pub async fn new( + conn: &zbus::Connection, + name_owners: &NameOwners, + executor: &calloop::futures::Scheduler<()>, + ) -> zbus::Result { let clients = Arc::new(Mutex::new(Clients::default())); - let clients_clone = clients.clone(); - let conn_cell = Arc::new(OnceLock::new()); - let conn_cell_clone = conn_cell.clone(); - let name_owners_cell = Arc::new(OnceLock::new()); - let name_owners_cell_clone = name_owners_cell.clone(); - let executor_clone = executor.clone(); - let _ = executor.schedule(async move { - match serve(clients_clone, &executor_clone).await { - Ok((conn, name_owners)) => { - conn_cell_clone.set(conn).unwrap(); - name_owners_cell_clone.set(name_owners).unwrap(); - } - Err(err) => { - tracing::error!("Failed to serve `org.freedesktop.a11y.Manager`: {err}"); - } - } - }); - Self { + + let keyboard_monitor = KeyboardMonitor { + clients: clients.clone(), + name_owners: name_owners.clone(), + }; + conn.object_server() + .at("/org/freedesktop/a11y/Manager", keyboard_monitor) + .await?; + conn.request_name("org.freedesktop.a11y.Manager").await?; + + Ok(Self { executor: executor.clone(), clients, active_virtual_mods: HashSet::new(), - conn: conn_cell, - name_owners: name_owners_cell, - } + conn: conn.clone(), + name_owners: name_owners.clone(), + }) } pub fn has_virtual_mod(&self, keysym: Keysym) -> bool { @@ -153,10 +150,6 @@ impl A11yKeyboardMonitorState { } pub fn key_event(&self, modifiers: &ModifiersState, keysym: &KeysymHandle, state: KeyState) { - let Some(conn) = self.conn.get() else { - return; - }; - let clients = self.clients.lock().unwrap(); for (unique_name, client) in clients.0.iter() { if !client.watched && !self.has_key_grab(modifiers, keysym.modified_sym()) { @@ -164,7 +157,7 @@ impl A11yKeyboardMonitorState { } let mut signal_context = - SignalEmitter::new(conn, "/org/freedesktop/a11y/Manager").unwrap(); + SignalEmitter::new(&self.conn, "/org/freedesktop/a11y/Manager").unwrap(); // Instead of sending signal to all clients, send only to authorized // clients with registed watches. signal_context = signal_context.set_destination(unique_name.clone().into()); @@ -194,13 +187,11 @@ impl A11yKeyboardMonitorState { pub fn refresh(&mut self) { // Remove clients and associated grabs when unique names are no longer // present on bus, or no longer hold approved name on bus. - if let Some(name_owners) = self.name_owners.get() { - self.clients - .lock() - .unwrap() - .0 - .retain(|k, _| name_owners.check_owner_no_poll(k, ALLOWED_NAMES)) - } + self.clients + .lock() + .unwrap() + .0 + .retain(|k, _| self.name_owners.check_owner_no_poll(k, ALLOWED_NAMES)) } } @@ -310,20 +301,3 @@ impl KeyboardMonitor { keycode: u16, ) -> zbus::Result<()>; } - -async fn serve( - clients: Arc>, - executor: &calloop::futures::Scheduler<()>, -) -> zbus::Result<(zbus::Connection, NameOwners)> { - let conn = zbus::Connection::session().await?; - let name_owners = NameOwners::new(&conn, executor).await?; - let keyboard_monitor = KeyboardMonitor { - clients, - name_owners: name_owners.clone(), - }; - conn.object_server() - .at("/org/freedesktop/a11y/Manager", keyboard_monitor) - .await?; - conn.request_name("org.freedesktop.a11y.Manager").await?; - Ok((conn, name_owners)) -} diff --git a/src/dbus/logind.rs b/src/dbus/logind.rs index b0c59170..53e3b2c0 100644 --- a/src/dbus/logind.rs +++ b/src/dbus/logind.rs @@ -1,24 +1,34 @@ use std::os::fd::OwnedFd; use anyhow::{Context, Result}; -use logind_zbus::manager::{InhibitType::HandleLidSwitch, ManagerProxyBlocking}; -use zbus::blocking::Connection; +use logind_zbus::manager::{InhibitType::HandleLidSwitch, ManagerProxy}; -pub fn inhibit_lid() -> Result { - let conn = Connection::system()?; - let proxy = ManagerProxyBlocking::new(&conn)?; - let fd = proxy.inhibit( - HandleLidSwitch, - "cosmic-comp", - "External output connected", - "block", - )?; +use crate::state::Common; + +pub fn inhibit_lid(common: &Common) -> Result { + let fd = futures_executor::block_on(async { + let conn = common.dbus_state.system_conn().await?; + let manager = ManagerProxy::new(conn).await?; + manager + .inhibit( + HandleLidSwitch, + "cosmic-comp", + "External output connected", + "block", + ) + .await + })?; Ok(fd.into()) } -pub fn lid_closed() -> Result { - let conn = Connection::system()?; - let proxy = ManagerProxyBlocking::new(&conn)?; - proxy.lid_closed().context("Failed to talk to logind") +pub fn lid_closed(common: &Common) -> Result { + futures_executor::block_on(async { + let conn = common.dbus_state.system_conn().await?; + let manager = ManagerProxy::new(conn).await?; + manager + .lid_closed() + .await + .context("Failed to talk to logind") + }) } diff --git a/src/dbus/mod.rs b/src/dbus/mod.rs index 475eaed0..f056738b 100644 --- a/src/dbus/mod.rs +++ b/src/dbus/mod.rs @@ -5,21 +5,110 @@ use crate::{ use anyhow::{Context, Result}; use calloop::{InsertError, LoopHandle, stream::StreamSource}; use cosmic_comp_config::output::comp::OutputState; -use futures_executor::block_on; -use std::collections::HashMap; +use std::{ + cell::{RefCell, RefMut}, + collections::HashMap, + rc::Rc, +}; use tracing::{error, warn}; -use zbus::blocking::{Connection, fdo::DBusProxy}; pub mod a11y_keyboard_monitor; +use a11y_keyboard_monitor::A11yKeyboardMonitorState; #[cfg(feature = "systemd")] pub mod logind; mod name_owners; mod power; -pub fn init(evlh: &LoopHandle<'static, State>) -> Result<()> { - match block_on(power::init()) { +#[derive(Clone, Debug)] +pub struct DBusState(Rc); + +#[derive(Debug)] +struct DBusStateInner { + evlh: LoopHandle<'static, State>, + executor: calloop::futures::Scheduler<()>, + session_conn: async_once_cell::OnceCell, + system_conn: async_once_cell::OnceCell, + a11y_keyboard_monitor: RefCell>, +} + +impl DBusState { + pub fn init(evlh: &LoopHandle<'static, State>) -> Self { + let (source, executor) = calloop::futures::executor().unwrap(); + let state = Self(Rc::new(DBusStateInner { + evlh: evlh.clone(), + executor, + session_conn: async_once_cell::OnceCell::new(), + system_conn: async_once_cell::OnceCell::new(), + a11y_keyboard_monitor: RefCell::new(None), + })); + evlh.insert_source(source, |_, _, _| {}).unwrap(); + let state_clone = state.clone(); + state.spawn(async move { + if let Err(err) = init_session(&state_clone).await { + tracing::error!("Failed to initialize session DBus connection: {}", err); + } + }); + let state_clone = state.clone(); + state.spawn(async move { + if let Err(err) = init_system(&state_clone).await { + tracing::error!("Failed to initialize system DBus connection: {}", err); + } + }); + state + } + + pub fn a11y_keyboard_monitor( + &self, + ) -> Option> { + RefMut::filter_map(self.0.a11y_keyboard_monitor.borrow_mut(), |x| x.as_mut()).ok() + } + + async fn session_conn(&self) -> zbus::Result<&zbus::Connection> { + self.0 + .session_conn + .get_or_try_init(zbus::Connection::session()) + .await + } + + async fn system_conn(&self) -> zbus::Result<&zbus::Connection> { + self.0 + .system_conn + .get_or_try_init(zbus::Connection::system()) + .await + } + + fn spawn(&self, fut: impl Future + 'static) { + let _ = self.0.executor.schedule(fut); + } +} + +async fn init_session(state: &DBusState) -> zbus::Result<()> { + let conn = state.session_conn().await?; + let name_owners = name_owners::NameOwners::new(conn, &state.0.executor).await?; + let a11y_keyboard_monitor_state = + A11yKeyboardMonitorState::new(conn, &name_owners, &state.0.executor).await?; + *state.0.a11y_keyboard_monitor.borrow_mut() = Some(a11y_keyboard_monitor_state); + Ok(()) +} + +async fn init_system(state: &DBusState) -> zbus::Result<()> { + let conn = state.system_conn().await?.clone(); + let evlh = state.0.evlh.clone(); + state.spawn(async move { + if let Err(err) = power_hot_plug_task(conn, evlh).await { + tracing::warn!(?err, "Failed to initialize dbus handlers"); + } + }); + Ok(()) +} + +async fn power_hot_plug_task( + conn: zbus::Connection, + evlh: LoopHandle<'static, State>, +) -> Result<()> { + match power::init(&conn).await { Ok(power_daemon) => { - if let Ok(stream) = block_on(power_daemon.receive_hot_plug_detect()) { + if let Ok(stream) = power_daemon.receive_hot_plug_detect().await { let source = StreamSource::new(stream).unwrap(); evlh.insert_source(source, |_, _, state| { let nodes = match &mut state.backend { @@ -63,20 +152,22 @@ pub fn init(evlh: &LoopHandle<'static, State>) -> Result<()> { /// Updated the D-Bus activation environment with `WAYLAND_DISPLAY` and /// `DISPLAY` variables. pub fn ready(common: &Common) -> Result<()> { - let conn = Connection::session()?; - let proxy = DBusProxy::new(&conn)?; - - proxy.update_activation_environment(HashMap::from([ - ("WAYLAND_DISPLAY", common.socket.to_str().unwrap()), - ( - "DISPLAY", - &common - .xwayland_state - .as_ref() - .map(|s| format!(":{}", s.display)) - .unwrap_or_default(), - ), - ]))?; + futures_executor::block_on(async { + let conn = common.dbus_state.session_conn().await?; + let dbus = zbus::fdo::DBusProxy::new(conn).await?; + dbus.update_activation_environment(HashMap::from([ + ("WAYLAND_DISPLAY", common.socket.to_str().unwrap()), + ( + "DISPLAY", + &common + .xwayland_state + .as_ref() + .map(|s| format!(":{}", s.display)) + .unwrap_or_default(), + ), + ])) + .await + })?; Ok(()) } diff --git a/src/dbus/power.rs b/src/dbus/power.rs index 346b8107..a20e5669 100644 --- a/src/dbus/power.rs +++ b/src/dbus/power.rs @@ -79,9 +79,8 @@ pub trait PowerDaemon { fn power_profile_switch(&self, profile: &str) -> zbus::Result<()>; } -pub async fn init() -> anyhow::Result> { - let conn = Connection::system().await?; - let proxy = PowerDaemonProxy::new(&conn).await?; +pub async fn init(conn: &Connection) -> anyhow::Result> { + let proxy = PowerDaemonProxy::new(conn).await?; proxy.0.introspect().await?; Ok(proxy) } diff --git a/src/input/mod.rs b/src/input/mod.rs index c4f422c3..fbce3b82 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1668,9 +1668,9 @@ impl State { .unwrap_or(false) }); - self.common - .a11y_keyboard_monitor_state - .key_event(modifiers, &handle, event.state()); + if let Some(a11y_keyboard_monitor) = self.common.dbus_state.a11y_keyboard_monitor() { + a11y_keyboard_monitor.key_event(modifiers, &handle, event.state()); + } // Leave move overview mode, if any modifier was released if let Some(Trigger::KeyboardMove(action_modifiers)) = @@ -1830,61 +1830,54 @@ impl State { ))); } - if event.state() == KeyState::Released { - let removed = self - .common - .a11y_keyboard_monitor_state - .remove_active_virtual_mod(handle.modified_sym()); - // If `Caps_Lock` is a virtual modifier, and is in locked state, clear it - if removed - && handle.modified_sym() == Keysym::Caps_Lock - && (modifiers.serialized.locked & 2) != 0 + if let Some(mut a11y_keyboard_monitor) = self.common.dbus_state.a11y_keyboard_monitor() { + if event.state() == KeyState::Released { + let removed = + a11y_keyboard_monitor.remove_active_virtual_mod(handle.modified_sym()); + // If `Caps_Lock` is a virtual modifier, and is in locked state, clear it + if removed + && handle.modified_sym() == Keysym::Caps_Lock + && (modifiers.serialized.locked & 2) != 0 + { + let seat = seat.clone(); + let key_code = event.key_code(); + self.common.event_loop_handle.insert_idle(move |state| { + if let Some(keyboard) = seat.get_keyboard() { + let serial = SERIAL_COUNTER.next_serial(); + let time = state.common.clock.now().as_millis(); + keyboard.input( + state, + key_code, + KeyState::Pressed, + serial, + time, + |_, _, _| FilterResult::<()>::Forward, + ); + let serial = SERIAL_COUNTER.next_serial(); + keyboard.input( + state, + key_code, + KeyState::Released, + serial, + time, + |_, _, _| FilterResult::<()>::Forward, + ); + } + }); + } + } else if event.state() == KeyState::Pressed + && a11y_keyboard_monitor.has_virtual_mod(handle.modified_sym()) { - let seat = seat.clone(); - let key_code = event.key_code(); - self.common.event_loop_handle.insert_idle(move |state| { - if let Some(keyboard) = seat.get_keyboard() { - let serial = SERIAL_COUNTER.next_serial(); - let time = state.common.clock.now().as_millis(); - keyboard.input( - state, - key_code, - KeyState::Pressed, - serial, - time, - |_, _, _| FilterResult::<()>::Forward, - ); - let serial = SERIAL_COUNTER.next_serial(); - keyboard.input( - state, - key_code, - KeyState::Released, - serial, - time, - |_, _, _| FilterResult::<()>::Forward, - ); - } - }); + a11y_keyboard_monitor.add_active_virtual_mod(handle.modified_sym()); + + tracing::debug!( + "active virtual mods: {:?}", + a11y_keyboard_monitor.active_virtual_mods() + ); + seat.supressed_keys().add(&handle, None); + + return FilterResult::Intercept(None); } - } else if event.state() == KeyState::Pressed - && self - .common - .a11y_keyboard_monitor_state - .has_virtual_mod(handle.modified_sym()) - { - self.common - .a11y_keyboard_monitor_state - .add_active_virtual_mod(handle.modified_sym()); - - tracing::debug!( - "active virtual mods: {:?}", - self.common - .a11y_keyboard_monitor_state - .active_virtual_mods() - ); - seat.supressed_keys().add(&handle, None); - - return FilterResult::Intercept(None); } // Skip released events for initially surpressed keys @@ -1911,12 +1904,10 @@ impl State { return FilterResult::Intercept(None); } - if event.state() == KeyState::Pressed - && (self.common.a11y_keyboard_monitor_state.has_keyboard_grab() - || self - .common - .a11y_keyboard_monitor_state - .has_key_grab(modifiers, handle.modified_sym())) + if let Some(a11y_keyboard_monitor) = self.common.dbus_state.a11y_keyboard_monitor() + && event.state() == KeyState::Pressed + && (a11y_keyboard_monitor.has_keyboard_grab() + || a11y_keyboard_monitor.has_key_grab(modifiers, handle.modified_sym())) { let modifiers_queue = seat.modifiers_shortcut_queue(); modifiers_queue.clear(); diff --git a/src/shell/mod.rs b/src/shell/mod.rs index da168360..76c4dd79 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1577,7 +1577,9 @@ impl Common { self.popups.cleanup(); self.toplevel_info_state.refresh(&self.workspace_state); self.refresh_idle_inhibit(); - self.a11y_keyboard_monitor_state.refresh(); + if let Some(mut a11y_keyboard_monitor) = self.dbus_state.a11y_keyboard_monitor() { + a11y_keyboard_monitor.refresh(); + } self.image_copy_capture_state.cleanup(); } diff --git a/src/state.rs b/src/state.rs index cfd38eda..26edade4 100644 --- a/src/state.rs +++ b/src/state.rs @@ -8,7 +8,7 @@ use crate::{ x11::X11State, }, config::{CompOutputConfig, Config, ScreenFilter}, - dbus::a11y_keyboard_monitor::A11yKeyboardMonitorState, + dbus::DBusState, input::{PointerFocusState, gestures::GestureState}, shell::{CosmicSurface, SeatExt, Shell, grabs::SeatMoveGrabState}, utils::prelude::OutputExt, @@ -233,7 +233,6 @@ pub struct Common { pub display_handle: DisplayHandle, pub event_loop_handle: LoopHandle<'static, State>, pub event_loop_signal: LoopSignal, - pub async_executor: calloop::futures::Scheduler<()>, pub popups: PopupManager, pub shell: Arc>, @@ -278,7 +277,7 @@ pub struct Common { pub xdg_decoration_state: XdgDecorationState, pub overlap_notify_state: OverlapNotifyState, pub a11y_state: A11yState, - pub a11y_keyboard_monitor_state: A11yKeyboardMonitorState, + pub dbus_state: DBusState, // shell-related wayland state pub xdg_shell_state: XdgShellState, @@ -726,16 +725,9 @@ impl State { ); let workspace_state = WorkspaceState::new(dh, client_not_sandboxed); - let (source, async_executor) = calloop::futures::executor().unwrap(); - handle.insert_source(source, |_, _, _| {}).unwrap(); - - if let Err(err) = crate::dbus::init(&handle) { - tracing::warn!(?err, "Failed to initialize dbus handlers"); - } - let a11y_state = A11yState::new::(dh, client_not_sandboxed); - let a11y_keyboard_monitor_state = A11yKeyboardMonitorState::new(&async_executor); + let dbus_state = DBusState::init(&handle); State { common: Common { @@ -744,7 +736,6 @@ impl State { display_handle: dh.clone(), event_loop_handle: handle, event_loop_signal: signal, - async_executor, popups: PopupManager::default(), shell, @@ -794,11 +785,11 @@ impl State { xdg_foreign_state, workspace_state, a11y_state, - a11y_keyboard_monitor_state, xwayland_scale: None, xwayland_state: None, xwayland_shell_state, pointer_focus_state: None, + dbus_state, #[cfg(feature = "systemd")] inhibit_lid_fd: None, @@ -841,7 +832,7 @@ impl State { if should_handle_lid { if self.common.inhibit_lid_fd.is_none() { - match crate::dbus::logind::inhibit_lid() { + match crate::dbus::logind::inhibit_lid(&self.common) { Ok(fd) => { debug!("Inhibiting lid switch"); self.common.inhibit_lid_fd = Some(fd); @@ -852,7 +843,8 @@ impl State { .iter() .find(|o| o.is_internal()) .cloned(); - let closed = crate::dbus::logind::lid_closed().unwrap_or(false); + let closed = + crate::dbus::logind::lid_closed(&self.common).unwrap_or(false); if closed { backend.disable_internal_output( From 51dd3bc66fe8d189264062d1e350abb96c1021d6 Mon Sep 17 00:00:00 2001 From: Hojjat Date: Fri, 5 Jun 2026 15:28:12 -0600 Subject: [PATCH 20/64] fix: pass the full window size to xwayland --- src/shell/element/surface.rs | 3 ++- src/shell/layout/floating/grabs/resize.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index f715bba3..c976b38b 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -238,7 +238,8 @@ impl CosmicSurface { toplevel.with_pending_state(|state| state.size = Some(geo.size.as_logical())) } WindowSurface::X11(surface) => { - let _ = surface.configure_with_sync(geo.as_logical(), None); + let _ = + surface.configure_with_sync(geo.as_logical() + surface.frame_extents(), None); } } } diff --git a/src/shell/layout/floating/grabs/resize.rs b/src/shell/layout/floating/grabs/resize.rs index 04363207..a1e63eac 100644 --- a/src/shell/layout/floating/grabs/resize.rs +++ b/src/shell/layout/floating/grabs/resize.rs @@ -543,7 +543,7 @@ impl ResizeSurfaceGrab { ); } WindowSurface::X11(surface) => { - let mut geometry = surface.geometry(); + let mut geometry = surface.last_configure(); geometry.loc += (location - new_location).as_logical(); let _ = surface.configure(geometry); } From b6b76e1d4a8edf4c6054b0d4af38c9e39e0a5ad6 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 7 May 2026 10:16:35 -0700 Subject: [PATCH 21/64] image-copy: Don't send redundantly pointer changes on fullscreen The code for sending pointer position changes for toplevels should handle this adequately; no need to also handle it here. --- src/input/mod.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index fbce3b82..1081b748 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -2481,6 +2481,7 @@ impl State { } } +// Output and workspace sessions for the given output fn cursor_sessions_for_output<'a>( shell: &'a Shell, output: &'a Output, @@ -2489,14 +2490,9 @@ fn cursor_sessions_for_output<'a>( .active_space(output) .into_iter() .flat_map(|workspace| { - let fullscreen_cursors: Vec<_> = workspace - .get_fullscreen_surfaces() - .flat_map(|f| f.surface.cursor_sessions()) - .collect(); workspace .cursor_sessions() .into_iter() - .chain(fullscreen_cursors) .chain(output.cursor_sessions()) }) } From 4a8931896f7e8aa60a85f7d4eb8016200bb0fe18 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 5 May 2026 18:44:26 -0700 Subject: [PATCH 22/64] image-copy: Subtract output position for pointer position sent to client --- src/input/mod.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 1081b748..13b39c20 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -640,11 +640,13 @@ impl State { for session in cursor_sessions_for_output(&shell, &output) { if let Some((geometry, offset)) = seat.cursor_geometry( - position.as_logical().to_buffer( - output.current_scale().fractional_scale(), - output.current_transform(), - &output_geometry.size.to_f64().as_logical(), - ), + (position - output_geometry.loc.to_f64()) + .as_logical() + .to_buffer( + output.current_scale().fractional_scale(), + output.current_transform(), + &output_geometry.size.to_f64().as_logical(), + ), self.common.clock.now(), ) { if session @@ -676,11 +678,11 @@ impl State { self.common.idle_notifier_state.notify_activity(&seat); notify_cursor_activity(self, &seat); let output = seat.active_output(); - let geometry = output.geometry(); - let position = geometry.loc.to_f64() + let output_geometry = output.geometry(); + let position = output_geometry.loc.to_f64() + smithay::backend::input::AbsolutePositionEvent::position_transformed( &event, - geometry.size.as_logical(), + output_geometry.size.as_logical(), ) .as_global(); let serial = SERIAL_COUNTER.next_serial(); @@ -702,11 +704,13 @@ impl State { let shell = self.common.shell.read(); for session in cursor_sessions_for_output(&shell, &output) { if let Some((geometry, offset)) = seat.cursor_geometry( - position.as_logical().to_buffer( - output.current_scale().fractional_scale(), - output.current_transform(), - &geometry.size.to_f64().as_logical(), - ), + (position - output_geometry.loc.to_f64()) + .as_logical() + .to_buffer( + output.current_scale().fractional_scale(), + output.current_transform(), + &output_geometry.size.to_f64().as_logical(), + ), self.common.clock.now(), ) { if session From 82dcd9a46a661cf495c1cf673a9f23229e51c84f Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 7 May 2026 15:02:16 -0700 Subject: [PATCH 23/64] image-copy: Don't send 0x0 buffer params for cursor capture Fixes panics in `xdg-desktop-portal-cosmic` when it tries to use cursor capture, and an application hides the cursor. --- src/input/mod.rs | 16 +++++++++++++--- .../handlers/image_copy_capture/render.rs | 6 +++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 13b39c20..519c54d0 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -64,7 +64,7 @@ use smithay::{ protocol::{wl_shm::Format as ShmFormat, wl_surface::WlSurface}, }, }, - utils::{Logical, Point, Rectangle, SERIAL_COUNTER, Serial}, + utils::{Logical, Point, Rectangle, SERIAL_COUNTER, Serial, Size}, wayland::{ compositor::CompositorHandler, image_copy_capture::{BufferConstraints, CursorSessionRef}, @@ -654,8 +654,13 @@ impl State { .map(|constraint| constraint.size != geometry.size) .unwrap_or(true) { + let mut cursor_size = geometry.size; + // Client shouldn't try to allocate 0x0 buffer + if cursor_size == Size::new(0, 0) { + cursor_size = Size::new(1, 1); + } session.update_constraints(BufferConstraints { - size: geometry.size, + size: cursor_size, shm: vec![ShmFormat::Argb8888], dma: None, }); @@ -718,8 +723,13 @@ impl State { .map(|constraint| constraint.size != geometry.size) .unwrap_or(true) { + let mut cursor_size = geometry.size; + // Client shouldn't try to allocate 0x0 buffer + if cursor_size == Size::new(0, 0) { + cursor_size = Size::new(1, 1); + } session.update_constraints(BufferConstraints { - size: geometry.size, + size: cursor_size, shm: vec![ShmFormat::Argb8888], dma: None, }); diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index 28ccd1cb..21370f31 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -753,11 +753,15 @@ pub fn render_cursor_to_buffer( seat: &Seat, ) { let buffer = frame.buffer(); - let cursor_size = seat + let mut cursor_size = seat .cursor_geometry((0.0, 0.0), state.common.clock.now()) .map(|(geo, _hotspot)| geo.size) .unwrap_or_else(|| Size::from((64, 64))); let buffer_size = buffer_dimensions(&buffer).unwrap(); + // Client shouldn't try to allocate 0x0 buffer + if cursor_size == Size::new(0, 0) { + cursor_size = Size::new(1, 1); + } if buffer_size != cursor_size { let constraints = BufferConstraints { size: cursor_size, From 8aea6cc15836ccb2eea1cdac6eb3b253eeb59d8f Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 28 May 2026 13:04:21 -0700 Subject: [PATCH 24/64] image-copy: Function for duplicated toplevel cursor enter/motion code Deduplicate this before making it more complicated. Also allows early returns to avoid deep nesting. And avoid duplicating calculations here for multiple sessions (though normally there won't be multiple capture sessions of same toplevel with cursor metadata, probably). --- src/shell/focus/target.rs | 76 +++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index ef749a64..b606dc17 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -225,6 +225,42 @@ impl PointerFocusTarget { _ => false, } } + + // Update image copy cursor position/hotspot for enter/motion event + fn update_image_copy_cursor_position( + &self, + seat: &Seat, + data: &mut State, + event: &PointerMotionEvent, + ) { + let Some(toplevel) = self.toplevel(&data.common.shell.read()) else { + return; + }; + let cursor_sessions = toplevel.cursor_sessions(); + if cursor_sessions.is_empty() { + return; + } + + let cursor_pos = Some( + event + .location + .to_buffer(1.0, Transform::Normal, &toplevel.geometry().size.to_f64()) + .to_i32_round(), + ); + + let cursor_hotspot = if let Some((_, hotspot)) = + seat.cursor_geometry((0.0, 0.0), Duration::from_millis(event.time as u64).into()) + { + hotspot + } else { + Point::from((0, 0)) + }; + + for session in cursor_sessions { + session.set_cursor_pos(cursor_pos); + session.set_cursor_hotspot(cursor_hotspot); + } + } } impl KeyboardFocusTarget { @@ -337,47 +373,11 @@ impl IsAlive for KeyboardFocusTarget { impl PointerTarget for PointerFocusTarget { fn enter(&self, seat: &Seat, data: &mut State, event: &PointerMotionEvent) { - let toplevel = self.toplevel(&data.common.shell.read()); - if let Some(element) = toplevel { - for session in element.cursor_sessions() { - session.set_cursor_pos(Some( - event - .location - .to_buffer(1.0, Transform::Normal, &element.geometry().size.to_f64()) - .to_i32_round(), - )); - if let Some((_, hotspot)) = seat - .cursor_geometry((0.0, 0.0), Duration::from_millis(event.time as u64).into()) - { - session.set_cursor_hotspot(hotspot); - } else { - session.set_cursor_hotspot((0, 0)); - } - } - } - + self.update_image_copy_cursor_position(seat, data, event); self.inner_pointer_target().enter(seat, data, event); } fn motion(&self, seat: &Seat, data: &mut State, event: &PointerMotionEvent) { - let toplevel = self.toplevel(&data.common.shell.read()); - if let Some(element) = toplevel { - for session in element.cursor_sessions() { - session.set_cursor_pos(Some( - event - .location - .to_buffer(1.0, Transform::Normal, &element.geometry().size.to_f64()) - .to_i32_round(), - )); - if let Some((_, hotspot)) = seat - .cursor_geometry((0.0, 0.0), Duration::from_millis(event.time as u64).into()) - { - session.set_cursor_hotspot(hotspot); - } else { - session.set_cursor_hotspot((0, 0)); - } - } - } - + self.update_image_copy_cursor_position(seat, data, event); self.inner_pointer_target().motion(seat, data, event); } fn relative_motion(&self, seat: &Seat, data: &mut State, event: &RelativeMotionEvent) { From 7a50625e78d519b64078b640ebb995a534915c62 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 28 May 2026 13:11:14 -0700 Subject: [PATCH 25/64] image-copy: Fix cursor pos for toplevels with SSD --- src/shell/focus/target.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index b606dc17..e0d10662 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -241,12 +241,17 @@ impl PointerFocusTarget { return; } - let cursor_pos = Some( - event - .location - .to_buffer(1.0, Transform::Normal, &toplevel.geometry().size.to_f64()) - .to_i32_round(), - ); + let cursor_pos = if let Some(_wl_surface) = self.wl_surface() { + Some( + event + .location + .to_buffer(1.0, Transform::Normal, &toplevel.geometry().size.to_f64()) + .to_i32_round(), + ) + } else { + // If cursor is in SSD, instead of a `wl_surface`, it is outside the captured bounds + None + }; let cursor_hotspot = if let Some((_, hotspot)) = seat.cursor_geometry((0.0, 0.0), Duration::from_millis(event.time as u64).into()) From 7d0d374fd8604ec88d1b4200e2a63537b50f0289 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 28 May 2026 13:16:00 -0700 Subject: [PATCH 26/64] image-copy: Adjust toplevel capture coordinate for window geometry --- src/shell/focus/target.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index e0d10662..21d5e0d0 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -37,7 +37,10 @@ use smithay::{ Client, DisplayHandle, Resource, backend::ObjectId, protocol::wl_surface::WlSurface, }, utils::{IsAlive, Logical, Point, Serial, Transform}, - wayland::{seat::WaylandFocus, selection::data_device::WlOfferData, session_lock::LockSurface}, + wayland::{ + compositor::with_states, seat::WaylandFocus, selection::data_device::WlOfferData, + session_lock::LockSurface, shell::xdg::SurfaceCachedState, + }, xwayland::{ X11Surface, xwm::{XwmId, XwmOfferData}, @@ -241,10 +244,19 @@ impl PointerFocusTarget { return; } - let cursor_pos = if let Some(_wl_surface) = self.wl_surface() { + let cursor_pos = if let Some(wl_surface) = self.wl_surface() { + let geometry_loc = with_states(&wl_surface, |states| { + states + .cached_state + .get::() + .current() + .geometry + .map(|g| g.loc.to_f64()) + }) + .unwrap_or_default(); + Some( - event - .location + (event.location - geometry_loc) .to_buffer(1.0, Transform::Normal, &toplevel.geometry().size.to_f64()) .to_i32_round(), ) From 84d2a9d297ab89331d56950b1d54913dbb1c4bde Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 8 Jun 2026 15:15:58 -0700 Subject: [PATCH 27/64] shell: Fix definition of `surface_tree_offset()` Using `with_surface_tree_downward()` is wrong because it will traverse other branches of the surface tree than the one containing `surface`, and add their offsets. It's simple enough to instead walk up the surface tree. --- src/shell/element/surface.rs | 46 +++++++++++++----------------------- 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index c976b38b..8495fe30 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -46,7 +46,7 @@ use smithay::{ }, wayland::{ compositor::{ - SubsurfaceCachedState, SurfaceData, TraversalAction, with_states, + SubsurfaceCachedState, SurfaceData, TraversalAction, get_parent, with_states, with_surface_tree_downward, }, seat::WaylandFocus, @@ -675,38 +675,24 @@ impl CosmicSurface { root: &WlSurface, surface: &WlSurface, ) -> Option> { - if root == surface { - return Some(Point::default()); - } - - let found = AtomicBool::new(false); - let mut found_offset = Point::::default(); - - with_surface_tree_downward( - root, - Point::::default(), - |s, states, parent_offset| { - let mut offset = *parent_offset; - if s != root { - offset += states + let mut offset = Point::::default(); + let mut parent = surface.clone(); + loop { + if parent == *root { + return Some(offset); + } else if let Some(s) = get_parent(&parent) { + offset += with_states(&parent, |states| { + states .cached_state .get::() .current() - .location; - } - TraversalAction::DoChildren(offset) - }, - |s, _, offset| { - if s == surface { - found_offset = *offset; - found.store(true, Ordering::SeqCst); - } - }, - |_, _, _| !found.load(Ordering::SeqCst), - ); - - if found.load(Ordering::SeqCst) { - return Some(found_offset); + .location + }); + parent = s; + } else { + // `parent` is now root of subsurface tree; `surface` is not a subsurface child of `root` + break; + } } for (popup, popup_offset) in PopupManager::popups_for_surface(root) { From 651877e622234a80fbbef045bbb05cdc33798728 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 8 Jun 2026 15:17:50 -0700 Subject: [PATCH 28/64] image-copy: Adjust cursor position for subsurface offset The `themed_window` example of `smithay-client-toolkit` is a good test for this. --- src/shell/focus/target.rs | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index 21d5e0d0..b0366f45 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -245,18 +245,25 @@ impl PointerFocusTarget { } let cursor_pos = if let Some(wl_surface) = self.wl_surface() { - let geometry_loc = with_states(&wl_surface, |states| { - states - .cached_state - .get::() - .current() - .geometry - .map(|g| g.loc.to_f64()) - }) - .unwrap_or_default(); - + let surface_offset = toplevel + .surface_offset(&wl_surface) + .unwrap_or(Point::from((0, 0))) + .to_f64(); + let geometry_loc = toplevel + .wl_surface() + .and_then(|s| { + with_states(&s, |states| { + states + .cached_state + .get::() + .current() + .geometry + .map(|g| g.loc.to_f64()) + }) + }) + .unwrap_or_default(); Some( - (event.location - geometry_loc) + (event.location - geometry_loc + surface_offset) .to_buffer(1.0, Transform::Normal, &toplevel.geometry().size.to_f64()) .to_i32_round(), ) From 94e9437c4eea78870b6ed9f8e4e930e45c7a6761 Mon Sep 17 00:00:00 2001 From: Hojjat Date: Wed, 22 Apr 2026 11:59:09 -0600 Subject: [PATCH 29/64] fix: use SOURCE_GIT_HASH when building for pop ci --- build.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.rs b/build.rs index b1cfaa97..94d2f89e 100644 --- a/build.rs +++ b/build.rs @@ -2,6 +2,10 @@ use std::process::Command; fn main() { + if let Ok(hash) = std::env::var("SOURCE_GIT_HASH") { + println!("cargo:rustc-env=GIT_HASH={}", hash); + return; + } if let Ok(output) = Command::new("git").args(["rev-parse", "HEAD"]).output() { let git_hash = String::from_utf8(output.stdout).unwrap(); println!("cargo:rustc-env=GIT_HASH={}", git_hash); From 5153d497e78c8e16ce8689eb7f03d2b4d0b250fd Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 9 Jun 2026 07:51:44 -0700 Subject: [PATCH 30/64] fix(dbus): Block on creation of zbus connection to avoid deadlock A fix for the issue reported in https://github.com/pop-os/cosmic-comp/pull/2450. Using `block_on` in some places for zbus calls is fine (that's what zbus's blocking API does; it has its own executor for background tasks), but this is problematic with `async_once_cell`. If we also use that in the calloop async executor. I think ideally we should avoid blocking the main thread on any async tasks, but for now we can just block in initialization here. --- Cargo.lock | 7 ------- Cargo.toml | 1 - src/dbus/mod.rs | 21 +++++++++------------ 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a94f5810..30f751b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -242,12 +242,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-once-cell" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" - [[package]] name = "async-process" version = "2.5.0" @@ -835,7 +829,6 @@ name = "cosmic-comp" version = "1.0.0" dependencies = [ "anyhow", - "async-once-cell", "bitflags 2.11.0", "calloop 0.14.4", "cgmath", diff --git a/Cargo.toml b/Cargo.toml index e548ad83..0d98c8c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,7 +83,6 @@ logind-zbus = { version = "5.3.2", optional = true } futures-executor = { version = "0.3.32" } futures-util = "0.3.32" cgmath = "0.18.0" -async-once-cell = "0.5.4" [dependencies.id_tree] branch = "feature/copy_clone" diff --git a/src/dbus/mod.rs b/src/dbus/mod.rs index f056738b..e2da75c3 100644 --- a/src/dbus/mod.rs +++ b/src/dbus/mod.rs @@ -26,19 +26,21 @@ pub struct DBusState(Rc); struct DBusStateInner { evlh: LoopHandle<'static, State>, executor: calloop::futures::Scheduler<()>, - session_conn: async_once_cell::OnceCell, - system_conn: async_once_cell::OnceCell, + session_conn: zbus::Result, + system_conn: zbus::Result, a11y_keyboard_monitor: RefCell>, } impl DBusState { pub fn init(evlh: &LoopHandle<'static, State>) -> Self { let (source, executor) = calloop::futures::executor().unwrap(); + let session_conn = futures_executor::block_on(zbus::Connection::session()); + let system_conn = futures_executor::block_on(zbus::Connection::system()); let state = Self(Rc::new(DBusStateInner { evlh: evlh.clone(), executor, - session_conn: async_once_cell::OnceCell::new(), - system_conn: async_once_cell::OnceCell::new(), + session_conn, + system_conn, a11y_keyboard_monitor: RefCell::new(None), })); evlh.insert_source(source, |_, _, _| {}).unwrap(); @@ -63,18 +65,13 @@ impl DBusState { RefMut::filter_map(self.0.a11y_keyboard_monitor.borrow_mut(), |x| x.as_mut()).ok() } + // TODO Lazy async init when we don't have anything blocking main thread async fn session_conn(&self) -> zbus::Result<&zbus::Connection> { - self.0 - .session_conn - .get_or_try_init(zbus::Connection::session()) - .await + self.0.session_conn.as_ref().map_err(|err| err.clone()) } async fn system_conn(&self) -> zbus::Result<&zbus::Connection> { - self.0 - .system_conn - .get_or_try_init(zbus::Connection::system()) - .await + self.0.system_conn.as_ref().map_err(|err| err.clone()) } fn spawn(&self, fut: impl Future + 'static) { From 0312f9a2017de92abf82386c979bee74d7a0255d Mon Sep 17 00:00:00 2001 From: Hojjat Date: Wed, 27 May 2026 18:16:38 -0600 Subject: [PATCH 31/64] fix: draw the focused window border for the current window only --- src/shell/workspace.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index ce5bf624..31682bf4 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1753,13 +1753,17 @@ impl Workspace { self.floating_layer .render::( renderer, - focused.as_ref().and_then(|target| { - if let FocusTarget::Window(mapped) = target { - Some(mapped) - } else { - None - } - }), + render_focus + .then(|| { + focused.as_ref().and_then(|target| { + if let FocusTarget::Window(mapped) = target { + Some(mapped) + } else { + None + } + }) + }) + .flatten(), resize_indicator.clone(), indicator_thickness, alpha, From 5ca8cc2cdf7c753580a686a58092eb497f326a6f Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Thu, 11 Jun 2026 13:59:36 +0200 Subject: [PATCH 32/64] feat(tiling-exceptions): add exception for Slack huddle preview --- data/tiling-exceptions.ron | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/tiling-exceptions.ron b/data/tiling-exceptions.ron index 14976eec..8413a188 100644 --- a/data/tiling-exceptions.ron +++ b/data/tiling-exceptions.ron @@ -48,5 +48,5 @@ (appid: "zoom", titles: [".*"]), (appid: "Tor Browser", titles: [".*"]), (appid: "^.*?action=join.*$", titles: [".*"]), - + (appid: "^(slack|com.slack.Slack)", titles: ["^.*?(Huddle Preview).*"]), ] From f95b62635d65aacfe56b46de4e028cc77c0ab507 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Thu, 11 Jun 2026 14:00:57 +0200 Subject: [PATCH 33/64] feat(tiling-exceptions): add exception for Thunderbird message compose dialog --- data/tiling-exceptions.ron | 1 + 1 file changed, 1 insertion(+) diff --git a/data/tiling-exceptions.ron b/data/tiling-exceptions.ron index 8413a188..2065c489 100644 --- a/data/tiling-exceptions.ron +++ b/data/tiling-exceptions.ron @@ -49,4 +49,5 @@ (appid: "Tor Browser", titles: [".*"]), (appid: "^.*?action=join.*$", titles: [".*"]), (appid: "^(slack|com.slack.Slack)", titles: ["^.*?(Huddle Preview).*"]), + (appid: "^(thunderbird|org.mozilla.thunderbird)(-esr|_esr)*", titles: ["^(Write:).*"]), ] From 31f317d16bcbe7cf81b185392d9bde4c1430669b Mon Sep 17 00:00:00 2001 From: Ilia Malanin Date: Tue, 9 Jun 2026 11:38:58 +0200 Subject: [PATCH 34/64] fix: skip dead windows in floating set_output --- src/shell/layout/floating/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 81c7a4da..f2f475a4 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -295,6 +295,7 @@ impl FloatingLayout { for mapped in self .space .elements() + .filter(|w| w.alive()) .cloned() .collect::>() .into_iter() @@ -309,7 +310,10 @@ impl FloatingLayout { None, ); } else { - let geometry = self.space.element_geometry(&mapped).unwrap().to_f64(); + let Some(geometry) = self.space.element_geometry(&mapped) else { + continue; + }; + let geometry = geometry.to_f64(); let new_loc = ( ((geometry.loc.x - old_output_geometry.loc.x).max(0.) / old_output_geometry.size.w From 52b3f930a88db6cdde73dbdd65a056cdc0efa40f Mon Sep 17 00:00:00 2001 From: Joseph Buckingham Date: Mon, 11 May 2026 15:51:59 -0700 Subject: [PATCH 35/64] fix: debug build pinning panic --- src/shell/workspace.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 31682bf4..37cda1b5 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -436,9 +436,9 @@ impl Workspace { } pub fn to_pinned(&self) -> Option { - debug_assert!(self.id.is_some()); let output = self.explicit_output().clone(); if self.pinned { + debug_assert!(self.id.is_some()); Some(PinnedWorkspace { output: cosmic_comp_config::workspace::OutputMatch { name: output.name, From e576a87f5b927a370281799e53bd71bce0f30bd2 Mon Sep 17 00:00:00 2001 From: Hojjat Date: Fri, 12 Jun 2026 18:01:09 -0600 Subject: [PATCH 36/64] Revert "fix: use SOURCE_GIT_HASH when building for pop ci" This reverts commit 94e9437c4eea78870b6ed9f8e4e930e45c7a6761. --- build.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build.rs b/build.rs index 94d2f89e..b1cfaa97 100644 --- a/build.rs +++ b/build.rs @@ -2,10 +2,6 @@ use std::process::Command; fn main() { - if let Ok(hash) = std::env::var("SOURCE_GIT_HASH") { - println!("cargo:rustc-env=GIT_HASH={}", hash); - return; - } if let Ok(output) = Command::new("git").args(["rev-parse", "HEAD"]).output() { let git_hash = String::from_utf8(output.stdout).unwrap(); println!("cargo:rustc-env=GIT_HASH={}", git_hash); From 8f5f7c897deaf0fac947ed1295692a35f318c8e6 Mon Sep 17 00:00:00 2001 From: Hojjat Date: Fri, 12 Jun 2026 18:02:42 -0600 Subject: [PATCH 37/64] fix: add git hash to .cargo/config when vendoring --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index cfe59669..16f59ee6 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,7 @@ vendor: mkdir -p .cargo cargo vendor | head -n -1 > .cargo/config echo 'directory = "vendor"' >> .cargo/config + [ -n "$(SOURCE_GIT_HASH)" ] && printf '\n[env]\nGIT_HASH = "%s"\n' "$(SOURCE_GIT_HASH)" >> .cargo/config || true tar pcf vendor.tar vendor rm -rf vendor From db0b1afeb5405d67c2993c5e5c4d15d82a120cb4 Mon Sep 17 00:00:00 2001 From: Robin Nehls Date: Sat, 30 May 2026 19:14:13 +0200 Subject: [PATCH 38/64] feat: allow naming pinned workspaces --- cosmic-comp-config/src/workspace.rs | 2 +- src/shell/mod.rs | 16 ++++++++++++++-- src/shell/workspace.rs | 4 ++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/cosmic-comp-config/src/workspace.rs b/cosmic-comp-config/src/workspace.rs index 08c3dc6c..62676264 100644 --- a/cosmic-comp-config/src/workspace.rs +++ b/cosmic-comp-config/src/workspace.rs @@ -63,5 +63,5 @@ pub struct PinnedWorkspace { pub output: OutputMatch, pub tiling_enabled: bool, pub id: Option, - // TODO: name + pub name: Option, } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 76c4dd79..89309e88 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -439,6 +439,11 @@ fn create_workspace_from_pinned( | WorkspaceCapabilities::Pin | WorkspaceCapabilities::Move, ); + + if let Some(ref name) = pinned.name { + state.set_workspace_name(&workspace_handle, name); + } + Workspace::from_pinned( pinned, workspace_handle, @@ -619,6 +624,7 @@ impl WorkspaceSet { state, self.workspaces.len() as u8 + 1, &workspace.handle, + workspace.name.as_deref(), // this method is only used by code paths related to dynamic workspaces, so this should be fine ); self.workspaces.push(workspace); @@ -680,7 +686,12 @@ impl WorkspaceSet { fn update_workspace_idxs(&self, state: &mut WorkspaceUpdateGuard<'_, State>) { for (i, workspace) in self.workspaces.iter().enumerate() { - workspace_set_idx(state, i as u8 + 1, &workspace.handle); + workspace_set_idx( + state, + i as u8 + 1, + &workspace.handle, + workspace.name.as_deref(), + ); } } @@ -5025,8 +5036,9 @@ fn workspace_set_idx( state: &mut WorkspaceUpdateGuard<'_, State>, idx: u8, handle: &WorkspaceHandle, + name: Option<&str>, ) { - state.set_workspace_name(handle, format!("{}", idx)); + state.set_workspace_name(handle, name.unwrap_or(&format!("{}", idx))); state.set_workspace_coordinates(handle, &[idx as u32]); } diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 37cda1b5..2b4db98c 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -111,6 +111,7 @@ pub struct Workspace { pub fullscreen_surfaces: Vec, pub pinned: bool, pub id: Option, + pub name: Option, pub handle: WorkspaceHandle, pub focus_stack: FocusStacks, @@ -386,6 +387,7 @@ impl Workspace { fullscreen_surfaces: Vec::new(), pinned: false, id: None, + name: None, handle, focus_stack: FocusStacks::default(), image_copy: ImageCopySessions::default(), @@ -419,6 +421,7 @@ impl Workspace { fullscreen_surfaces: Vec::new(), pinned: true, id: pinned.id.clone(), + name: pinned.name.clone(), handle, focus_stack: FocusStacks::default(), image_copy: ImageCopySessions::default(), @@ -446,6 +449,7 @@ impl Workspace { }, tiling_enabled: self.tiling_enabled, id: self.id.clone(), + name: self.name.clone(), }) } else { None From d4c5714c108aef348da66a8a0175c42898eb643e Mon Sep 17 00:00:00 2001 From: Gavin John Date: Thu, 4 Jun 2026 20:00:49 -0700 Subject: [PATCH 39/64] perf(toplevel_info): optimize send_toplevel_to_client PR #1279 tried to throttle `send_toplevel_to_client`, which was rejected. This takes (what I think is) a better approach, and instead makes some optimizations. We delay grabbing the mutex until it's actually needed, and we also checks to see if the state actually needs to be resent. This resolves some lock contention, and also avoids having to generate the state in the first place. Since `wayland-rs` uses a single mutex to lock the entire state, this both massively reduces lock contention and avoids making (apparently quite a lot of) unnecessary computation. I decided to give this one to AI since I'm a bit busy right now (although I am still manually writing the commit messages and descriptions). I think it generally did a pretty good job, although I did have to make a few manual tweaks. While I don't have any empirical data, cosmic-comp idle CPU has gone down from ~3% all-core to 0-0.5%, even stress testing it with far more windows open. I can't say it *liked* opening 30-some windows in 2 seconds, but after it finished opening them all, it was still buttery smooth. Overall very happy with the results. Assisted-by: Claude:claude-4-opus --- src/wayland/protocols/toplevel_info.rs | 137 +++++++++++++++---------- 1 file changed, 85 insertions(+), 52 deletions(-) diff --git a/src/wayland/protocols/toplevel_info.rs b/src/wayland/protocols/toplevel_info.rs index 4643aca6..2dc76347 100644 --- a/src/wayland/protocols/toplevel_info.rs +++ b/src/wayland/protocols/toplevel_info.rs @@ -495,35 +495,18 @@ where .unwrap() .lock() .unwrap(); - let foreign_toplevel_handle = state.foreign_handle.as_ref(); let mut changed = false; - if handle_state.title != window.title() { - handle_state.title = window.title(); - if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE { - instance.title(handle_state.title.clone()); - } - if let Some(handle) = foreign_toplevel_handle { - handle.send_title(&handle_state.title); - } - changed = true; - } - if handle_state.app_id != window.app_id() { - handle_state.app_id = window.app_id(); - if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE { - instance.app_id(handle_state.app_id.clone()); - } - if let Some(handle) = foreign_toplevel_handle { - handle.send_app_id(&handle_state.app_id); - } - changed = true; - } + let new_title = (handle_state.title != window.title()).then(|| window.title()); + let new_app_id = (handle_state.app_id != window.app_id()).then(|| window.app_id()); - if handle_state.states.as_ref().is_none_or(|states| { + let new_states = if handle_state.states.as_ref().is_none_or(|states| { (states.contains(&States::Maximized) != window.is_maximized()) || (states.contains(&States::Fullscreen) != window.is_fullscreen()) || (states.contains(&States::Activated) != window.is_activated()) || (states.contains(&States::Minimized) != window.is_minimized()) + || (instance.version() >= zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE + && states.contains(&States::Sticky) != window.is_sticky()) }) { let mut states = Vec::new(); if window.is_maximized() { @@ -543,8 +526,64 @@ where { states.push(States::Sticky); } - handle_state.states = Some(states.clone()); + Some(states) + } else { + None + }; + let geometry_changed = if !window.is_resizing() { + let geometry = window.global_geometry(); + if handle_state.geometry != geometry { + handle_state.geometry = geometry; + true + } else { + false + } + } else { + false + }; + + let outputs_changed = state.outputs != handle_state.outputs + || handle_state.wl_outputs.iter().any(|o| !o.is_alive()); + + let workspaces_changed = state.workspaces != handle_state.workspaces; + + if new_title.is_none() + && new_app_id.is_none() + && new_states.is_none() + && !geometry_changed + && !outputs_changed + && !workspaces_changed + { + return false; + } + + let foreign_toplevel_handle = state.foreign_handle.as_ref(); + + if let Some(title) = new_title { + handle_state.title = title; + if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE { + instance.title(handle_state.title.clone()); + } + if let Some(handle) = foreign_toplevel_handle { + handle.send_title(&handle_state.title); + } + changed = true; + } + + if let Some(app_id) = new_app_id { + handle_state.app_id = app_id; + if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE { + instance.app_id(handle_state.app_id.clone()); + } + if let Some(handle) = foreign_toplevel_handle { + handle.send_app_id(&handle_state.app_id); + } + changed = true; + } + + if let Some(states) = new_states { + handle_state.states = Some(states.clone()); let states = states .iter() .flat_map(|state| (*state as u32).to_ne_bytes()) @@ -553,17 +592,9 @@ where changed = true; } - let mut geometry_changed = false; - if !window.is_resizing() { - let geometry = window.global_geometry(); - if handle_state.geometry != geometry { - handle_state.geometry = geometry; - changed = true; - geometry_changed = true; - } - } - - if let Ok(client) = dh.get_client(instance.id()) { + if (outputs_changed || geometry_changed) + && let Ok(client) = dh.get_client(instance.id()) + { handle_state.outputs = state.outputs.clone(); let handle_state = &mut *handle_state; @@ -599,27 +630,29 @@ where }); } - for new_workspace in state - .workspaces - .iter() - .filter(|w| !handle_state.workspaces.contains(w)) - { - for handle in workspace_state.raw_ext_workspace_handles(new_workspace, &instance.id()) { - instance.ext_workspace_enter(handle); - changed = true; + if workspaces_changed { + for new_workspace in state + .workspaces + .iter() + .filter(|w| !handle_state.workspaces.contains(w)) + { + for handle in workspace_state.raw_ext_workspace_handles(new_workspace, &instance.id()) { + instance.ext_workspace_enter(handle); + changed = true; + } } - } - for old_workspace in handle_state - .workspaces - .iter() - .filter(|w| !state.workspaces.contains(w)) - { - for handle in workspace_state.raw_ext_workspace_handles(old_workspace, &instance.id()) { - instance.ext_workspace_leave(handle); - changed = true; + for old_workspace in handle_state + .workspaces + .iter() + .filter(|w| !state.workspaces.contains(w)) + { + for handle in workspace_state.raw_ext_workspace_handles(old_workspace, &instance.id()) { + instance.ext_workspace_leave(handle); + changed = true; + } } + handle_state.workspaces = state.workspaces.clone(); } - handle_state.workspaces = state.workspaces.clone(); if changed { if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE { From 836ccbaa0bd29150914247e9d3f98f7c4081c924 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 16 Jun 2026 15:32:42 +0200 Subject: [PATCH 40/64] shell/stack: Cleanup previous_index on remove --- src/shell/element/stack.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index d2f9c949..4d198cfa 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -249,6 +249,10 @@ impl CosmicStack { window.set_tiled(false); p.active.fetch_min(windows.len() - 1, Ordering::SeqCst); + p.previous_index + .lock() + .unwrap() + .take_if(|(_, idx)| *idx >= windows.len()); }); self.0 .resize(Size::from((self.active().geometry().size.w, TAB_HEIGHT))); @@ -276,6 +280,10 @@ impl CosmicStack { window.set_tiled(false); p.active.fetch_min(windows.len() - 1, Ordering::SeqCst); + p.previous_index + .lock() + .unwrap() + .take_if(|(_, idx)| *idx >= windows.len()); Some(window) }); From 69bb35e33615a3f398b87b4136116a59eed85292 Mon Sep 17 00:00:00 2001 From: SAY-5 Date: Sat, 2 May 2026 11:14:21 -0700 Subject: [PATCH 41/64] floating: don't panic if window disappears mid-unminimize Signed-off-by: SAY-5 --- src/shell/layout/floating/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index f2f475a4..df94fd86 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -642,7 +642,14 @@ impl FloatingLayout { self.space .map_element(mapped.clone(), position.as_logical(), true); self.space.refresh(); - let target_geometry = self.space.element_geometry(&mapped).unwrap().as_local(); + // The window can disappear between `map_element` and the geometry + // lookup if the client crashes mid-unminimize (race observed in + // pop-os/cosmic-comp#2332). Skip the animation rather than panic — + // the window is already gone, there's nothing to animate. + let Some(target_geometry) = self.space.element_geometry(&mapped) else { + return; + }; + let target_geometry = target_geometry.as_local(); self.animations.insert( mapped, From aff506bb7bab5d3280b1c910c096b3dd1bf3098c Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Tue, 26 May 2026 21:46:12 -0700 Subject: [PATCH 42/64] fix(floating): allow remapping minimized windows Signed-off-by: Sai Asish Y --- src/shell/layout/floating/mod.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index df94fd86..e3829e58 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -623,6 +623,10 @@ impl FloatingLayout { from: Rectangle, position: Point, ) { + if !mapped.alive() { + return; + } + let output = self.space.outputs().next().unwrap().clone(); let layers = layer_map_for_output(&output); let geometry = layers.non_exclusive_zone().as_local(); @@ -642,14 +646,7 @@ impl FloatingLayout { self.space .map_element(mapped.clone(), position.as_logical(), true); self.space.refresh(); - // The window can disappear between `map_element` and the geometry - // lookup if the client crashes mid-unminimize (race observed in - // pop-os/cosmic-comp#2332). Skip the animation rather than panic — - // the window is already gone, there's nothing to animate. - let Some(target_geometry) = self.space.element_geometry(&mapped) else { - return; - }; - let target_geometry = target_geometry.as_local(); + let target_geometry = self.space.element_geometry(&mapped).unwrap().as_local(); self.animations.insert( mapped, From dddf51b44158ecd5d44e85504da4c3d659f78c2c Mon Sep 17 00:00:00 2001 From: Richard Chennault Date: Tue, 16 Jun 2026 13:41:01 -0700 Subject: [PATCH 43/64] kms: skip lease resume when drm device activation fails in resume_session() When smithay's DrmDevice::activate() returns an error (e.g. because drmSetMaster() failed during VT resume), resume_session() was only logging the error and then proceeding to resume DRM leases on a device that has no master. This leaves the compositor in a broken render state where every atomic page flip returns EPERM. Fix: continue to the next device on activate() failure. The device surface stays inactive and lease resume is deferred. On the next ActivateSession event (next VT switch to this TTY), resume_session() will be called again and activate() will succeed once the seat manager has granted DRM master. Reproducer: hybrid GPU system (Intel iGPU render + NVIDIA display), VT switch away from and back to COSMIC session. On return, drmSetMaster races the seat notification; activate() fails; without this fix the compositor floods journald with EPERM at 60fps. Fixes: pop-os#2331, pop-os#2302 --- src/backend/kms/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 3a0d8c8c..694d6ce7 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -374,6 +374,7 @@ impl State { for device in backend.drm_devices.values_mut() { if let Err(err) = device.drm.lock().activate(true) { error!(?err, "Failed to resume drm device"); + continue; } if let Some(lease_state) = device.inner.leasing_global.as_mut() { lease_state.resume::(); From 821b431973fbc1ce2fae1c2a347eadbbae833246 Mon Sep 17 00:00:00 2001 From: "Anthony T. Lannutti" Date: Mon, 15 Jun 2026 00:30:35 -0500 Subject: [PATCH 44/64] feat: separate logind feature from systemd Move logind-zbus to a dedicated 'logind' feature that is independent of the 'systemd' feature. This allows non-systemd users (e.g., OpenRC with elogind) to access lid switch inhibition and lid status detection without requiring the full systemd stack. The 'systemd' feature now depends on 'logind' to maintain backward compatibility, so existing users are unaffected. Feature configuration: - default: ["systemd"] - logind: ["logind-zbus"] - systemd: ["libsystemd", "logind", "tracing-journald"] Resolves #2473 Coding-Agent: OpenCode Model: claude-sonnet-4-5 --- Cargo.toml | 3 ++- src/dbus/mod.rs | 2 +- src/input/mod.rs | 2 +- src/state.rs | 8 ++++---- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0d98c8c1..56b9e2c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -117,7 +117,8 @@ optional = true [features] debug = ["egui", "egui_plot", "smithay-egui", "anyhow/backtrace"] default = ["systemd"] -systemd = ["libsystemd", "logind-zbus"] +logind = ["logind-zbus"] +systemd = ["libsystemd", "logind"] profile-with-tracy = ["profiling/profile-with-tracy", "tracy-client/default"] profile-with-tracy-gpu = ["profile-with-tracy", "smithay/tracy_gpu_profiling"] diff --git a/src/dbus/mod.rs b/src/dbus/mod.rs index e2da75c3..5b9754c3 100644 --- a/src/dbus/mod.rs +++ b/src/dbus/mod.rs @@ -14,7 +14,7 @@ use tracing::{error, warn}; pub mod a11y_keyboard_monitor; use a11y_keyboard_monitor::A11yKeyboardMonitorState; -#[cfg(feature = "systemd")] +#[cfg(feature = "logind")] pub mod logind; mod name_owners; mod power; diff --git a/src/input/mod.rs b/src/input/mod.rs index 519c54d0..a2863d09 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1600,7 +1600,7 @@ impl State { } InputEvent::Special(_) => {} InputEvent::SwitchToggle { event } => { - #[cfg(feature = "systemd")] + #[cfg(feature = "logind")] if event.switch() == Some(Switch::Lid) && self.common.inhibit_lid_fd.is_some() { let backend = self.backend.lock(); let output = backend diff --git a/src/state.rs b/src/state.rs index 26edade4..5b84492f 100644 --- a/src/state.rs +++ b/src/state.rs @@ -116,7 +116,7 @@ use smithay::{ }; use tracing::warn; -#[cfg(feature = "systemd")] +#[cfg(feature = "logind")] use std::os::fd::OwnedFd; use std::{ @@ -292,7 +292,7 @@ pub struct Common { pub xwayland_shell_state: XWaylandShellState, pub pointer_focus_state: Option, - #[cfg(feature = "systemd")] + #[cfg(feature = "logind")] pub inhibit_lid_fd: Option, pub with_xwayland: bool, @@ -791,7 +791,7 @@ impl State { pointer_focus_state: None, dbus_state, - #[cfg(feature = "systemd")] + #[cfg(feature = "logind")] inhibit_lid_fd: None, with_xwayland, @@ -816,7 +816,7 @@ impl State { } fn update_inhibitor_locks(&mut self) { - #[cfg(feature = "systemd")] + #[cfg(feature = "logind")] { use smithay::backend::session::Session; use tracing::{debug, error, warn}; From b6cab38528fe9baf8ecad27ca60605ff9c933623 Mon Sep 17 00:00:00 2001 From: Skygrango Date: Sun, 24 May 2026 01:47:57 +0800 Subject: [PATCH 45/64] wayland: implement pointer_warp_v1 --- src/state.rs | 2 ++ src/wayland/handlers/mod.rs | 1 + src/wayland/handlers/pointer_warp.rs | 44 ++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 src/wayland/handlers/pointer_warp.rs diff --git a/src/state.rs b/src/state.rs index 5b84492f..18de36ba 100644 --- a/src/state.rs +++ b/src/state.rs @@ -86,6 +86,7 @@ use smithay::{ output::OutputManagerState, pointer_constraints::PointerConstraintsState, pointer_gestures::PointerGesturesState, + pointer_warp::PointerWarpManager, presentation::PresentationState, seat::WaylandFocus, security_context::{SecurityContext, SecurityContextState}, @@ -671,6 +672,7 @@ impl State { XWaylandKeyboardGrabState::new::(dh); let xwayland_shell_state = XWaylandShellState::new::(dh); PointerConstraintsState::new::(dh); + PointerWarpManager::new::(dh); PointerGesturesState::new::(dh); TabletManagerState::new::(dh); SecurityContextState::new::(dh, client_has_no_security_context); diff --git a/src/wayland/handlers/mod.rs b/src/wayland/handlers/mod.rs index 8f6547cf..34d063d0 100644 --- a/src/wayland/handlers/mod.rs +++ b/src/wayland/handlers/mod.rs @@ -25,6 +25,7 @@ pub mod output_configuration; pub mod output_power; pub mod overlap_notify; pub mod pointer_constraints; +pub mod pointer_warp; pub mod primary_selection; pub mod seat; pub mod security_context; diff --git a/src/wayland/handlers/pointer_warp.rs b/src/wayland/handlers/pointer_warp.rs new file mode 100644 index 00000000..971ab583 --- /dev/null +++ b/src/wayland/handlers/pointer_warp.rs @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use crate::state::State; +use smithay::{ + input::pointer::PointerHandle, + reexports::wayland_server::protocol::{wl_pointer::WlPointer, wl_surface::WlSurface}, + utils::{Logical, Point, Serial}, + wayland::pointer_warp::PointerWarpHandler, +}; + +impl PointerWarpHandler for State { + fn warp_pointer( + &mut self, + surface: WlSurface, + pointer: WlPointer, + pos: Point, + serial: Serial, + ) { + let Some(resource_handle) = PointerHandle::::from_resource(&pointer) else { + return; + }; + + let shell = self.common.shell.read(); + + let pointer_handle = shell.seats.iter().find_map(|seat| { + if let Some(pointer_handle) = seat.get_pointer() + && resource_handle == pointer_handle + && pointer_handle.last_enter() == Some(serial) + && let Some(keyboard) = seat.get_keyboard() + && let Some(keyboard_focus) = keyboard.current_focus() + && keyboard_focus.has_surface(&shell, &surface) + { + return Some(pointer_handle); + } + None + }); + + drop(shell); + + if let Some(pointer_handle) = pointer_handle { + self.apply_cursor_hint(&surface, &pointer_handle, pos); + } + } +} From 296b6bee23ed886140e10cd573747e04b171a570 Mon Sep 17 00:00:00 2001 From: Skygrango Date: Fri, 5 Jun 2026 23:35:29 +0800 Subject: [PATCH 46/64] wayland: reject cursor hint requests that are outside the surface bound --- src/input/mod.rs | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index a2863d09..da3014ae 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -2393,7 +2393,7 @@ impl State { }); if let Some((output, geometry, surface_offset)) = found { - let mut pos_in_element = location + surface_offset.to_f64(); + let pos_in_element = location + surface_offset.to_f64(); let window_size = geometry.size.to_f64(); let is_legal = |p: Point| { @@ -2417,29 +2417,13 @@ impl State { let workspace_origin = output.geometry().loc.to_f64(); let origin = geometry.loc.to_f64(); - if !is_legal(pos_in_element) { - let original_global = pointer.current_location(); - - let original_pos_in_element = Point::new( - original_global.x - workspace_origin.x - origin.x, - original_global.y - workspace_origin.y - origin.y, - ); - - let y_only_pos = Point::new(original_pos_in_element.x, pos_in_element.y); - let x_only_pos = Point::new(pos_in_element.x, original_pos_in_element.y); - - if is_legal(y_only_pos) { - pos_in_element = y_only_pos; - } else if is_legal(x_only_pos) { - pos_in_element = x_only_pos; - } else { - pos_in_element = original_pos_in_element; - } + if is_legal(pos_in_element) { + let x = workspace_origin.x + origin.x + pos_in_element.x; + let y = workspace_origin.y + origin.y + pos_in_element.y; + Some((Point::new(x, y), output.clone())) + } else { + None } - - let x = workspace_origin.x + origin.x + pos_in_element.x; - let y = workspace_origin.y + origin.y + pos_in_element.y; - Some((Point::new(x, y), output.clone())) } else { None } From 106b50293b6daf0225a4511ad9e55167af7c8c7d Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 11 Jun 2026 17:09:58 +0200 Subject: [PATCH 47/64] wayland/activation: Fix requests without active outputs --- src/wayland/handlers/xdg_activation.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/wayland/handlers/xdg_activation.rs b/src/wayland/handlers/xdg_activation.rs index 03c3c629..ad8ce0d0 100644 --- a/src/wayland/handlers/xdg_activation.rs +++ b/src/wayland/handlers/xdg_activation.rs @@ -21,6 +21,10 @@ pub enum ActivationContext { Workspace(WorkspaceHandle), } +// It may happen that we get activation-requests, while all outputs are disabled. +// In these cases we won't be able to determine workspaces for windows and thus +// need to handle the corresponding code paths defensively. + impl XdgActivationHandler for State { fn activation_state(&mut self) -> &mut XdgActivationState { &mut self.common.xdg_activation_state @@ -52,7 +56,12 @@ impl XdgActivationHandler for State { }); let output = seat.active_output(); let mut shell = self.common.shell.write(); - let workspace = shell.active_space_mut(&output).unwrap(); + let Some(workspace) = shell.active_space_mut(&output) else { + debug!(?token, "created urgent token for privileged client"); + data.user_data + .insert_if_missing(move || ActivationContext::UrgentOnly); + return true; + }; let handle = workspace.handle; data.user_data .insert_if_missing(move || ActivationContext::Workspace(handle)); @@ -86,7 +95,11 @@ impl XdgActivationHandler for State { if valid { let output = seat.active_output(); let mut shell = self.common.shell.write(); - let workspace = shell.active_space_mut(&output).unwrap(); + let Some(workspace) = shell.active_space_mut(&output) else { + data.user_data + .insert_if_missing(|| ActivationContext::UrgentOnly); + return true; + }; let handle = workspace.handle; data.user_data .insert_if_missing(move || ActivationContext::Workspace(handle)); @@ -128,7 +141,6 @@ impl XdgActivationHandler for State { let Some((target_workspace, _)) = shell.workspace_for_surface(&surface) else { - // surface not found, maybe log warning? return; }; From f032e7dbd51cdaf8b219e58127db7f09814f7997 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 12 Jun 2026 16:17:53 +0200 Subject: [PATCH 48/64] shell/focus: Order sticky windows before fullscreen windows --- src/shell/focus/order.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/shell/focus/order.rs b/src/shell/focus/order.rs index 6d021e6c..d02b310e 100644 --- a/src/shell/focus/order.rs +++ b/src/shell/focus/order.rs @@ -231,9 +231,7 @@ fn render_input_order_internal( } // sticky window popups - if !has_focused_fullscreen { - callback(Stage::StickyPopups(&set.sticky_layer))?; - } + callback(Stage::StickyPopups(&set.sticky_layer))?; } if element_filter != ElementFilter::LayerShellOnly { @@ -313,11 +311,11 @@ fn render_input_order_internal( for (layer, location) in layer_surfaces(output, Layer::Top, element_filter) { callback(Stage::LayerSurface { layer, location })?; } + } - // sticky windows - if element_filter != ElementFilter::LayerShellOnly { - callback(Stage::Sticky(&set.sticky_layer))?; - } + // sticky windows + if element_filter != ElementFilter::LayerShellOnly { + callback(Stage::Sticky(&set.sticky_layer))?; } if element_filter != ElementFilter::LayerShellOnly { From aac1e19f08a016ade349569fbf8c0305761de20b Mon Sep 17 00:00:00 2001 From: Ilia Malanin Date: Wed, 17 Jun 2026 12:52:47 +0200 Subject: [PATCH 49/64] fix: set_rectangle leak --- src/wayland/protocols/toplevel_management.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/wayland/protocols/toplevel_management.rs b/src/wayland/protocols/toplevel_management.rs index 9ec5132a..f252fde5 100644 --- a/src/wayland/protocols/toplevel_management.rs +++ b/src/wayland/protocols/toplevel_management.rs @@ -237,11 +237,10 @@ where window_from_handle::<::Window>(toplevel).unwrap(); if let Some(toplevel_state) = window.user_data().get::() { let mut toplevel_state = toplevel_state.lock().unwrap(); - if width == 0 && height == 0 { - toplevel_state - .rectangles - .retain(|(s, _)| s.id() != surface.id()); - } else { + toplevel_state + .rectangles + .retain(|(s, _)| s.id() != surface.id()); + if width != 0 || height != 0 { toplevel_state.rectangles.push(( surface.downgrade(), Rectangle::new((x, y).into(), (width, height).into()), From 650768211fbfa3bcde7f7b52c152cc630dbe6404 Mon Sep 17 00:00:00 2001 From: Frederic Laing Date: Fri, 26 Jun 2026 16:08:59 +0200 Subject: [PATCH 50/64] fix: keep windows visible until overview commits a buffer --- src/utils/quirks.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/utils/quirks.rs b/src/utils/quirks.rs index 2ecda9fd..e0954fd4 100644 --- a/src/utils/quirks.rs +++ b/src/utils/quirks.rs @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only -use smithay::{desktop::layer_map_for_output, output::Output}; +use smithay::{ + backend::renderer::utils::with_renderer_surface_state, desktop::layer_map_for_output, + output::Output, +}; /// Layer shell namespace used by `cosmic-workspaces` // TODO: Avoid special case, or add protocol to expose required behavior @@ -10,5 +13,14 @@ pub const WORKSPACE_OVERVIEW_NAMESPACE: &str = "cosmic-workspace-overview"; pub fn workspace_overview_is_open(output: &Output) -> bool { layer_map_for_output(output) .layers() - .any(|s| s.namespace() == WORKSPACE_OVERVIEW_NAMESPACE) + .filter(|s| s.namespace() == WORKSPACE_OVERVIEW_NAMESPACE) + // Only consider the overview open once it has committed a buffer. The + // surface is inserted into the layer map on its initial (bufferless) + // commit, so checking for the namespace alone hides all toplevels for a + // frame before the overview has anything to draw, briefly flashing the + // bare wallpaper. + .any(|s| { + with_renderer_surface_state(s.wl_surface(), |state| state.buffer().is_some()) + .unwrap_or(false) + }) } From ccc4f36065086b4e5b044003baf3669366bb9da3 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Mon, 29 Jun 2026 17:20:02 +0200 Subject: [PATCH 51/64] chore: update smithay --- Cargo.lock | 548 +++++++++++----------- Cargo.toml | 2 +- src/backend/winit.rs | 2 +- src/shell/element/stack.rs | 55 ++- src/shell/element/window.rs | 41 +- src/shell/focus/target.rs | 43 +- src/shell/grabs/delay.rs | 30 +- src/shell/grabs/menu/mod.rs | 34 +- src/shell/grabs/mod.rs | 88 ++-- src/shell/grabs/moving.rs | 23 +- src/shell/layout/floating/grabs/resize.rs | 30 +- src/shell/layout/mod.rs | 2 +- src/shell/layout/tiling/grabs/resize.rs | 64 +-- src/shell/zoom.rs | 59 +-- src/utils/iced/mod.rs | 43 +- src/xwayland.rs | 1 + 16 files changed, 513 insertions(+), 552 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30f751b1..1bc58958 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -429,9 +429,9 @@ dependencies = [ [[package]] name = "block2" -version = "0.5.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" dependencies = [ "objc2", ] @@ -522,20 +522,6 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" -[[package]] -name = "calloop" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" -dependencies = [ - "bitflags 2.11.0", - "log", - "polling", - "rustix 0.38.44", - "slab", - "thiserror 1.0.69", -] - [[package]] name = "calloop" version = "0.14.4" @@ -551,25 +537,13 @@ dependencies = [ "tracing", ] -[[package]] -name = "calloop-wayland-source" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" -dependencies = [ - "calloop 0.13.0", - "rustix 0.38.44", - "wayland-backend", - "wayland-client", -] - [[package]] name = "calloop-wayland-source" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" dependencies = [ - "calloop 0.14.4", + "calloop", "rustix 1.1.4", "wayland-backend", "wayland-client", @@ -819,7 +793,7 @@ dependencies = [ "bitflags 2.11.0", "cosmic-protocols", "libc", - "smithay-client-toolkit 0.20.0", + "smithay-client-toolkit", "wayland-client", "wayland-protocols", ] @@ -830,7 +804,7 @@ version = "1.0.0" dependencies = [ "anyhow", "bitflags 2.11.0", - "calloop 0.14.4", + "calloop", "cgmath", "clap_lex", "cosmic-comp-config", @@ -903,7 +877,7 @@ version = "1.0.0" source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "atomicwrites", - "calloop 0.14.4", + "calloop", "cosmic-config-derive", "cosmic-settings-daemon", "dirs", @@ -1010,7 +984,7 @@ dependencies = [ "rustc-hash 2.1.1", "self_cell", "skrifa 0.40.0", - "smol_str 0.3.5", + "smol_str", "swash", "sys-locale", "unicode-bidi", @@ -1234,10 +1208,14 @@ dependencies = [ ] [[package]] -name = "dispatch" -version = "0.2.0" +name = "dispatch2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.11.0", + "objc2", +] [[package]] name = "displaydoc" @@ -1267,7 +1245,7 @@ dependencies = [ "bitflags 2.11.0", "mime 0.1.0", "raw-window-handle", - "smithay-client-toolkit 0.20.0", + "smithay-client-toolkit", "smithay-clipboard", ] @@ -2291,7 +2269,7 @@ dependencies = [ "raw-window-handle", "rustc-hash 2.1.1", "serde", - "smol_str 0.3.5", + "smol_str", "thiserror 2.0.18", "web-time", "window_clipboard", @@ -2801,6 +2779,16 @@ dependencies = [ "winnow 0.6.24", ] +[[package]] +name = "keyboard-types" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbe853b403ae61a04233030ae8a79d94975281ed9770a1f9e246732b534b28d" +dependencies = [ + "bitflags 2.11.0", + "serde", +] + [[package]] name = "keyframe" version = "1.1.1" @@ -3558,96 +3546,59 @@ dependencies = [ "objc_id", ] -[[package]] -name = "objc-sys" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" - [[package]] name = "objc2" -version = "0.5.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" dependencies = [ - "objc-sys", "objc2-encode", ] [[package]] name = "objc2-app-kit" -version = "0.2.2" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ "bitflags 2.11.0", "block2", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.11.0", "libc", - "objc2", - "objc2-core-data", - "objc2-core-image", - "objc2-foundation", - "objc2-quartz-core", + "objc2-core-foundation", ] [[package]] -name = "objc2-cloud-kit" -version = "0.2.2" +name = "objc2-core-video" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" dependencies = [ "bitflags 2.11.0", - "block2", - "objc2", - "objc2-core-location", - "objc2-foundation", -] - -[[package]] -name = "objc2-contacts" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" -dependencies = [ - "block2", - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-data" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" -dependencies = [ - "bitflags 2.11.0", - "block2", - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-image" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" -dependencies = [ - "block2", - "objc2", - "objc2-foundation", - "objc2-metal", -] - -[[package]] -name = "objc2-core-location" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" -dependencies = [ - "block2", - "objc2", - "objc2-contacts", - "objc2-foundation", + "objc2-core-foundation", + "objc2-core-graphics", ] [[package]] @@ -3658,106 +3609,25 @@ checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" [[package]] name = "objc2-foundation" -version = "0.2.2" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" -dependencies = [ - "bitflags 2.11.0", - "block2", - "dispatch", - "libc", - "objc2", -] - -[[package]] -name = "objc2-link-presentation" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" -dependencies = [ - "block2", - "objc2", - "objc2-app-kit", - "objc2-foundation", -] - -[[package]] -name = "objc2-metal" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.11.0", "block2", "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-quartz-core" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" -dependencies = [ - "bitflags 2.11.0", - "block2", - "objc2", - "objc2-foundation", - "objc2-metal", -] - -[[package]] -name = "objc2-symbols" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" -dependencies = [ - "objc2", - "objc2-foundation", + "objc2-core-foundation", ] [[package]] name = "objc2-ui-kit" -version = "0.2.2" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ "bitflags 2.11.0", - "block2", "objc2", - "objc2-cloud-kit", - "objc2-core-data", - "objc2-core-image", - "objc2-core-location", - "objc2-foundation", - "objc2-link-presentation", - "objc2-quartz-core", - "objc2-symbols", - "objc2-uniform-type-identifiers", - "objc2-user-notifications", -] - -[[package]] -name = "objc2-uniform-type-identifiers" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" -dependencies = [ - "block2", - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-user-notifications" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" -dependencies = [ - "bitflags 2.11.0", - "block2", - "objc2", - "objc2-core-location", + "objc2-core-foundation", "objc2-foundation", ] @@ -4382,15 +4252,6 @@ dependencies = [ "font-types 0.11.0", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.5.18" @@ -4469,6 +4330,18 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +[[package]] +name = "reis" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3fedd2777cde52c1be5e572efbec485eac7b801c47820eda388d4f13b9c4b" +dependencies = [ + "calloop", + "enumflags2", + "log", + "rustix 1.1.4", +] + [[package]] name = "renderdoc-sys" version = "1.1.0" @@ -4924,14 +4797,14 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/smithay/smithay.git?rev=85f83ab#85f83ab61711e725aa1f3481df4f4dc5370e925b" +source = "git+https://github.com/smithay/smithay.git?rev=8eb4076#8eb4076cad8705c56f5e3bac8577b83576b6ce76" dependencies = [ "aliasable", "appendlist", "ash", "atomic_float", "bitflags 2.11.0", - "calloop 0.14.4", + "calloop", "cc", "cgmath", "cursor-icon", @@ -4953,6 +4826,7 @@ dependencies = [ "pkg-config", "profiling", "rand 0.9.2", + "reis", "rustix 1.1.4", "scopeguard", "sha2", @@ -4974,31 +4848,6 @@ dependencies = [ "xkbcommon 0.9.0", ] -[[package]] -name = "smithay-client-toolkit" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" -dependencies = [ - "bitflags 2.11.0", - "calloop 0.13.0", - "calloop-wayland-source 0.3.0", - "cursor-icon", - "libc", - "log", - "memmap2", - "rustix 0.38.44", - "thiserror 1.0.69", - "wayland-backend", - "wayland-client", - "wayland-csd-frame", - "wayland-cursor", - "wayland-protocols", - "wayland-protocols-wlr", - "wayland-scanner", - "xkeysym", -] - [[package]] name = "smithay-client-toolkit" version = "0.20.0" @@ -5007,8 +4856,8 @@ checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" dependencies = [ "bitflags 2.11.0", "bytemuck", - "calloop 0.14.4", - "calloop-wayland-source 0.4.1", + "calloop", + "calloop-wayland-source", "cursor-icon", "libc", "log", @@ -5036,7 +4885,7 @@ source = "git+https://github.com/pop-os/smithay-clipboard?tag=sctk-0.20#859b02c8 dependencies = [ "libc", "raw-window-handle", - "smithay-client-toolkit 0.20.0", + "smithay-client-toolkit", "wayland-backend", ] @@ -5057,15 +4906,6 @@ dependencies = [ "xkbcommon 0.8.0", ] -[[package]] -name = "smol_str" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" -dependencies = [ - "serde", -] - [[package]] name = "smol_str" version = "0.3.5" @@ -6655,50 +6495,223 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winit" -version = "0.30.12" +version = "0.31.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732" +checksum = "2879d2854d1a43e48f67322d4bd097afcb6eb8f8f775c8de0260a71aea1df1aa" dependencies = [ - "ahash", - "android-activity", - "atomic-waker", "bitflags 2.11.0", - "block2", - "bytemuck", - "calloop 0.13.0", "cfg_aliases", - "concurrent-queue", - "core-foundation", - "core-graphics", "cursor-icon", "dpi", - "js-sys", "libc", - "memmap2", + "raw-window-handle", + "rustix 1.1.4", + "smol_str", + "tracing", + "winit-android", + "winit-appkit", + "winit-common", + "winit-core", + "winit-orbital", + "winit-uikit", + "winit-wayland", + "winit-web", + "winit-win32", + "winit-x11", +] + +[[package]] +name = "winit-android" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d9c0d2cd93efec3a9f9ad819cfaf0834782403af7c0d248c784ec0c61761df" +dependencies = [ + "android-activity", + "bitflags 2.11.0", + "dpi", "ndk", + "raw-window-handle", + "smol_str", + "tracing", + "winit-core", +] + +[[package]] +name = "winit-appkit" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21310ca07851a49c348e0c2cc768e36b52ca65afda2c2354d78ed4b90074d8aa" +dependencies = [ + "bitflags 2.11.0", + "block2", + "dispatch2", + "dpi", "objc2", "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-video", + "objc2-foundation", + "raw-window-handle", + "smol_str", + "tracing", + "winit-common", + "winit-core", +] + +[[package]] +name = "winit-common" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45375fbac4cbb77260d83a30b1f9d8105880dbac99a9ae97f56656694680ff69" +dependencies = [ + "memmap2", + "objc2", + "objc2-core-foundation", + "smol_str", + "tracing", + "winit-core", + "x11-dl", + "xkbcommon-dl", +] + +[[package]] +name = "winit-core" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4f0ccd7abb43740e2c6124ac7cae7d865ecec74eec63783e8922577ac232583" +dependencies = [ + "bitflags 2.11.0", + "cursor-icon", + "dpi", + "keyboard-types", + "raw-window-handle", + "smol_str", + "web-time", +] + +[[package]] +name = "winit-orbital" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ea1fb262e7209f265f12bd0cc792c399b14355675e65531e9c8a87db287d46" +dependencies = [ + "bitflags 2.11.0", + "dpi", + "orbclient", + "raw-window-handle", + "redox_syscall 0.5.18", + "smol_str", + "tracing", + "winit-core", +] + +[[package]] +name = "winit-uikit" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680a356e798837d8eb274d4556e83bceaf81698194e31aafc5cfb8a9f2fab643" +dependencies = [ + "bitflags 2.11.0", + "block2", + "dispatch2", + "dpi", + "objc2", + "objc2-core-foundation", "objc2-foundation", "objc2-ui-kit", - "orbclient", - "percent-encoding", - "pin-project", "raw-window-handle", - "redox_syscall 0.4.1", - "rustix 0.38.44", - "smithay-client-toolkit 0.19.2", - "smol_str 0.2.2", + "smol_str", + "tracing", + "winit-common", + "winit-core", +] + +[[package]] +name = "winit-wayland" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce5afb2ba07da603f84b722c95f9f9396d2cedae3944fb6c0cda4a6f88de545" +dependencies = [ + "ahash", + "bitflags 2.11.0", + "calloop", + "cursor-icon", + "dpi", + "libc", + "memmap2", + "raw-window-handle", + "rustix 1.1.4", + "smithay-client-toolkit", + "smol_str", "tracing", - "unicode-segmentation", - "wasm-bindgen", - "wasm-bindgen-futures", "wayland-backend", "wayland-client", "wayland-protocols", "wayland-protocols-plasma", + "winit-common", + "winit-core", +] + +[[package]] +name = "winit-web" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2490a953fb776fbbd5e295d54f1c3847f4f15b6c3929ec53c09acda6487a92" +dependencies = [ + "atomic-waker", + "bitflags 2.11.0", + "concurrent-queue", + "cursor-icon", + "dpi", + "js-sys", + "pin-project", + "raw-window-handle", + "smol_str", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", "web-sys", "web-time", - "windows-sys 0.52.0", + "winit-core", +] + +[[package]] +name = "winit-win32" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644ea78af0e858aa3b092e5d1c67c41995a98220c81813f1353b28bc8bb91eaa" +dependencies = [ + "bitflags 2.11.0", + "cursor-icon", + "dpi", + "raw-window-handle", + "smol_str", + "tracing", + "unicode-segmentation", + "windows-sys 0.59.0", + "winit-core", +] + +[[package]] +name = "winit-x11" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa5b600756534c7041aa93cd0d244d44b09fca1b89e202bd1cd80dd9f3636c46" +dependencies = [ + "bitflags 2.11.0", + "bytemuck", + "calloop", + "cursor-icon", + "dpi", + "libc", + "percent-encoding", + "raw-window-handle", + "rustix 1.1.4", + "smol_str", + "tracing", + "winit-common", + "winit-core", "x11-dl", "x11rb", "xkbcommon-dl", @@ -6840,6 +6853,7 @@ dependencies = [ "once_cell", "rustix 1.1.4", "x11rb-protocol", + "xcursor", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 56b9e2c9..f1a04f02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -143,4 +143,4 @@ cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch cosmic-client-toolkit = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } [patch.crates-io] -smithay = { git = "https://github.com/smithay/smithay.git", rev = "85f83ab" } +smithay = { git = "https://github.com/smithay/smithay.git", rev = "8eb4076" } diff --git a/src/backend/winit.rs b/src/backend/winit.rs index a35b0c98..0ff249a7 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -26,7 +26,7 @@ use smithay::{ calloop::{EventLoop, ping}, wayland_protocols::wp::presentation_time::server::wp_presentation_feedback, wayland_server::DisplayHandle, - winit::platform::pump_events::PumpStatus, + winit::event_loop::pump_events::PumpStatus, }, utils::Transform, wayland::{dmabuf::DmabufFeedbackBuilder, presentation::Refresh}, diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 4d198cfa..9f8584f5 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -61,8 +61,8 @@ use smithay::{ PointerTarget, RelativeMotionEvent, }, touch::{ - DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget, - UpEvent, + DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, + TouchTarget, UpEvent, }, }, output::Output, @@ -79,7 +79,7 @@ use std::{ hash::Hash, sync::{ Arc, LazyLock, Mutex, - atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering}, + atomic::{AtomicBool, AtomicU8, AtomicU32, AtomicUsize, Ordering}, }, }; @@ -114,6 +114,7 @@ pub struct CosmicStackInternal { scroll_to_focus: AtomicBool, previous_keyboard: AtomicUsize, pointer_entered: AtomicU8, + touch_serial: AtomicU32, reenter: AtomicBool, potential_drag: Mutex>, override_alive: AtomicBool, @@ -171,6 +172,7 @@ impl CosmicStack { scroll_to_focus: AtomicBool::new(false), previous_keyboard: AtomicUsize::new(0), pointer_entered: AtomicU8::new(0), + touch_serial: AtomicU32::new(0), reenter: AtomicBool::new(false), potential_drag: Mutex::new(None), override_alive: AtomicBool::new(true), @@ -1815,55 +1817,62 @@ impl PointerTarget for CosmicStack { } impl TouchTarget for CosmicStack { - fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent, seq: Serial) { + fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent) { let mut event = event.clone(); let active_window_geo = self.0.with_program(|p| { p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry() }); event.location -= active_window_geo.loc.to_f64(); - TouchTarget::down(&self.0, seat, data, &event, seq) + self.0 + .with_program(|p| p.touch_serial.store(event.serial.into(), Ordering::Release)); + TouchTarget::down(&self.0, seat, data, &event) } - fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent, seq: Serial) { - TouchTarget::up(&self.0, seat, data, event, seq) + fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent) { + TouchTarget::up(&self.0, seat, data, event) } - fn motion(&self, seat: &Seat, data: &mut State, event: &TouchMotionEvent, seq: Serial) { + fn motion(&self, seat: &Seat, data: &mut State, event: &TouchMotionEvent) { let mut event = event.clone(); let active_window_geo = self.0.with_program(|p| { p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry() }); event.location -= active_window_geo.loc.to_f64(); - TouchTarget::motion(&self.0, seat, data, &event, seq); + TouchTarget::motion(&self.0, seat, data, &event); if event.location.y < 0.0 || event.location.y > TAB_HEIGHT as f64 || event.location.x < 64.0 || event.location.x > (active_window_geo.size.w as f64 - 64.0) { - self.start_drag(data, seat, seq); + self.start_drag( + data, + seat, + self.0 + .with_program(|p| p.touch_serial.load(Ordering::Acquire)) + .into(), + ); } } - fn frame(&self, seat: &Seat, data: &mut State, seq: Serial) { - TouchTarget::frame(&self.0, seat, data, seq) + fn frame(&self, seat: &Seat, data: &mut State, frame: FrameMarker) { + TouchTarget::frame(&self.0, seat, data, frame) } - fn cancel(&self, seat: &Seat, data: &mut State, seq: Serial) { - TouchTarget::cancel(&self.0, seat, data, seq) + fn cancel(&self, seat: &Seat, data: &mut State, frame: FrameMarker) { + TouchTarget::cancel(&self.0, seat, data, frame) } - fn shape(&self, seat: &Seat, data: &mut State, event: &ShapeEvent, seq: Serial) { - TouchTarget::shape(&self.0, seat, data, event, seq) + fn shape(&self, seat: &Seat, data: &mut State, event: &ShapeEvent) { + TouchTarget::shape(&self.0, seat, data, event) } - fn orientation( - &self, - _seat: &Seat, - _data: &mut State, - _event: &OrientationEvent, - _seq: Serial, - ) { + fn orientation(&self, seat: &Seat, data: &mut State, event: &OrientationEvent) { + TouchTarget::orientation(&self.0, seat, data, event) + } + + fn last_frame(&self, seat: &Seat, data: &mut State) -> Option { + TouchTarget::last_frame(&self.0, seat, data) } } diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index dd45de4b..0adbfab7 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -47,8 +47,8 @@ use smithay::{ GestureSwipeUpdateEvent, MotionEvent, PointerTarget, RelativeMotionEvent, }, touch::{ - DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget, - UpEvent, + DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, + TouchTarget, UpEvent, }, }, output::Output, @@ -1190,43 +1190,42 @@ impl PointerTarget for CosmicWindow { } impl TouchTarget for CosmicWindow { - fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent, seq: Serial) { + fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent) { let mut event = event.clone(); self.0.with_program(|p| { event.location -= p.window.geometry().loc.to_f64(); }); - TouchTarget::down(&self.0, seat, data, &event, seq) + TouchTarget::down(&self.0, seat, data, &event) } - fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent, seq: Serial) { - TouchTarget::up(&self.0, seat, data, event, seq) + fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent) { + TouchTarget::up(&self.0, seat, data, event) } - fn motion(&self, seat: &Seat, data: &mut State, event: &TouchMotionEvent, seq: Serial) { + fn motion(&self, seat: &Seat, data: &mut State, event: &TouchMotionEvent) { let mut event = event.clone(); event.location -= self.0.with_program(|p| p.window.geometry().loc.to_f64()); - TouchTarget::motion(&self.0, seat, data, &event, seq) + TouchTarget::motion(&self.0, seat, data, &event) } - fn frame(&self, seat: &Seat, data: &mut State, seq: Serial) { - TouchTarget::frame(&self.0, seat, data, seq) + fn frame(&self, seat: &Seat, data: &mut State, frame: FrameMarker) { + TouchTarget::frame(&self.0, seat, data, frame) } - fn cancel(&self, seat: &Seat, data: &mut State, seq: Serial) { - TouchTarget::cancel(&self.0, seat, data, seq) + fn cancel(&self, seat: &Seat, data: &mut State, frame: FrameMarker) { + TouchTarget::cancel(&self.0, seat, data, frame) } - fn shape(&self, seat: &Seat, data: &mut State, event: &ShapeEvent, seq: Serial) { - TouchTarget::shape(&self.0, seat, data, event, seq) + fn shape(&self, seat: &Seat, data: &mut State, event: &ShapeEvent) { + TouchTarget::shape(&self.0, seat, data, event) } - fn orientation( - &self, - _seat: &Seat, - _data: &mut State, - _event: &OrientationEvent, - _seq: Serial, - ) { + fn orientation(&self, seat: &Seat, data: &mut State, event: &OrientationEvent) { + TouchTarget::orientation(&self.0, seat, data, event) + } + + fn last_frame(&self, seat: &Seat, data: &mut State) -> Option { + TouchTarget::last_frame(&self.0, seat, data) } } diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index b0366f45..fff77282 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -29,8 +29,8 @@ use smithay::{ MotionEvent as PointerMotionEvent, PointerTarget, RelativeMotionEvent, }, touch::{ - DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget, - UpEvent, + DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, + TouchTarget, UpEvent, }, }, reexports::wayland_server::{ @@ -498,39 +498,36 @@ impl PointerTarget for PointerFocusTarget { } impl TouchTarget for PointerFocusTarget { - fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent, seq: Serial) { - self.inner_touch_target().down(seat, data, event, seq); + fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent) { + self.inner_touch_target().down(seat, data, event); } - fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent, seq: Serial) { - self.inner_touch_target().up(seat, data, event, seq); + fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent) { + self.inner_touch_target().up(seat, data, event); } - fn motion(&self, seat: &Seat, data: &mut State, event: &TouchMotionEvent, seq: Serial) { - self.inner_touch_target().motion(seat, data, event, seq); + fn motion(&self, seat: &Seat, data: &mut State, event: &TouchMotionEvent) { + self.inner_touch_target().motion(seat, data, event); } - fn frame(&self, seat: &Seat, data: &mut State, seq: Serial) { - self.inner_touch_target().frame(seat, data, seq); + fn frame(&self, seat: &Seat, data: &mut State, frame: FrameMarker) { + self.inner_touch_target().frame(seat, data, frame); } - fn cancel(&self, seat: &Seat, data: &mut State, seq: Serial) { - self.inner_touch_target().cancel(seat, data, seq); + fn cancel(&self, seat: &Seat, data: &mut State, frame: FrameMarker) { + self.inner_touch_target().cancel(seat, data, frame); } - fn shape(&self, seat: &Seat, data: &mut State, event: &ShapeEvent, seq: Serial) { - self.inner_touch_target().shape(seat, data, event, seq); + fn shape(&self, seat: &Seat, data: &mut State, event: &ShapeEvent) { + self.inner_touch_target().shape(seat, data, event); } - fn orientation( - &self, - seat: &Seat, - data: &mut State, - event: &OrientationEvent, - seq: Serial, - ) { - self.inner_touch_target() - .orientation(seat, data, event, seq); + fn orientation(&self, seat: &Seat, data: &mut State, event: &OrientationEvent) { + self.inner_touch_target().orientation(seat, data, event); + } + + fn last_frame(&self, seat: &Seat, data: &mut State) -> Option { + self.inner_touch_target().last_frame(seat, data) } } diff --git a/src/shell/grabs/delay.rs b/src/shell/grabs/delay.rs index dbf98d91..96d33dc6 100644 --- a/src/shell/grabs/delay.rs +++ b/src/shell/grabs/delay.rs @@ -202,19 +202,12 @@ impl> TouchGrab for DelayGrab { handle: &mut TouchInnerHandle<'_, State>, focus: Option<(::TouchFocus, Point)>, event: &DownEvent, - seq: Serial, ) { - handle.down(data, focus, event, seq); + handle.down(data, focus, event); } - fn up( - &mut self, - data: &mut State, - handle: &mut TouchInnerHandle<'_, State>, - event: &UpEvent, - seq: Serial, - ) { - handle.up(data, event, seq); + fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) { + handle.up(data, event); if event.slot == TouchGrab::start_data(self).slot { handle.unset_grab(self, data); @@ -227,9 +220,8 @@ impl> TouchGrab for DelayGrab { handle: &mut TouchInnerHandle<'_, State>, focus: Option<(::TouchFocus, Point)>, event: &TouchMotionEvent, - seq: Serial, ) { - handle.motion(data, focus, event, seq); + handle.motion(data, focus, event); let distance = self.start_data.distance(event.location); if distance >= 1. @@ -245,12 +237,12 @@ impl> TouchGrab for DelayGrab { } } - fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) { - handle.frame(data, seq) + fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) { + handle.frame(data) } - fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) { - handle.cancel(data, seq); + fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) { + handle.cancel(data); handle.unset_grab(self, data); } @@ -259,9 +251,8 @@ impl> TouchGrab for DelayGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &ShapeEvent, - seq: Serial, ) { - handle.shape(data, event, seq) + handle.shape(data, event) } fn orientation( @@ -269,9 +260,8 @@ impl> TouchGrab for DelayGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &OrientationEvent, - seq: Serial, ) { - handle.orientation(data, event, seq); + handle.orientation(data, event); } fn start_data(&self) -> &TouchGrabStartData { diff --git a/src/shell/grabs/menu/mod.rs b/src/shell/grabs/menu/mod.rs index 094f941c..14a9f917 100644 --- a/src/shell/grabs/menu/mod.rs +++ b/src/shell/grabs/menu/mod.rs @@ -718,7 +718,6 @@ impl TouchGrab for MenuGrab { handle: &mut TouchInnerHandle<'_, State>, _focus: Option<(PointerFocusTarget, Point)>, event: &DownEvent, - seq: Serial, ) { { let mut guard = self.elements.lock().unwrap(); @@ -753,21 +752,15 @@ impl TouchGrab for MenuGrab { time: event.time, }; if element.touch_entered.is_none() { - TouchTarget::down(&element.iced, &self.seat, data, &new_event, seq); + TouchTarget::down(&element.iced, &self.seat, data, &new_event); element.touch_entered = Some(event.slot); } } } - handle.down(data, None, event, seq); + handle.down(data, None, event); } - fn up( - &mut self, - data: &mut State, - handle: &mut TouchInnerHandle<'_, State>, - event: &UpEvent, - seq: Serial, - ) { + fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) { { let elements = self.elements.lock().unwrap(); for element in elements.iter().filter(|elem| { @@ -775,7 +768,7 @@ impl TouchGrab for MenuGrab { .as_ref() .is_some_and(|slot| *slot == event.slot) }) { - TouchTarget::up(&element.iced, &self.seat, data, event, seq); + TouchTarget::up(&element.iced, &self.seat, data, event); } } handle.unset_grab(self, data); @@ -787,7 +780,6 @@ impl TouchGrab for MenuGrab { handle: &mut TouchInnerHandle<'_, State>, _focus: Option<(PointerFocusTarget, Point)>, event: &TouchMotionEvent, - seq: Serial, ) { { let elements = self.elements.lock().unwrap(); @@ -796,24 +788,24 @@ impl TouchGrab for MenuGrab { .as_ref() .is_some_and(|slot| *slot == event.slot) }) { - TouchTarget::motion(&element.iced, &self.seat, data, event, seq); + TouchTarget::motion(&element.iced, &self.seat, data, event); } } - handle.motion(data, None, event, seq); + handle.motion(data, None, event); } - fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) { - handle.frame(data, seq); + fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) { + handle.frame(data); } - fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) { + fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) { { let mut elements = self.elements.lock().unwrap(); for element in elements.iter_mut() { let _ = element.touch_entered.take(); } } - handle.cancel(data, seq); + handle.cancel(data); } fn shape( @@ -821,9 +813,8 @@ impl TouchGrab for MenuGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &smithay::input::touch::ShapeEvent, - seq: Serial, ) { - handle.shape(data, event, seq); + handle.shape(data, event); } fn orientation( @@ -831,9 +822,8 @@ impl TouchGrab for MenuGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &smithay::input::touch::OrientationEvent, - seq: Serial, ) { - handle.orientation(data, event, seq); + handle.orientation(data, event); } fn start_data(&self) -> &TouchGrabStartData { diff --git a/src/shell/grabs/mod.rs b/src/shell/grabs/mod.rs index 0c5448c1..515916fd 100644 --- a/src/shell/grabs/mod.rs +++ b/src/shell/grabs/mod.rs @@ -387,24 +387,17 @@ impl TouchGrab for ResizeGrab { handle: &mut TouchInnerHandle<'_, State>, focus: Option<(PointerFocusTarget, Point)>, event: &DownEvent, - seq: Serial, ) { match self { - ResizeGrab::Floating(grab) => TouchGrab::down(grab, data, handle, focus, event, seq), - ResizeGrab::Tiling(grab) => TouchGrab::down(grab, data, handle, focus, event, seq), + ResizeGrab::Floating(grab) => TouchGrab::down(grab, data, handle, focus, event), + ResizeGrab::Tiling(grab) => TouchGrab::down(grab, data, handle, focus, event), } } - fn up( - &mut self, - data: &mut State, - handle: &mut TouchInnerHandle<'_, State>, - event: &UpEvent, - seq: Serial, - ) { + fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) { match self { - ResizeGrab::Floating(grab) => TouchGrab::up(grab, data, handle, event, seq), - ResizeGrab::Tiling(grab) => TouchGrab::up(grab, data, handle, event, seq), + ResizeGrab::Floating(grab) => TouchGrab::up(grab, data, handle, event), + ResizeGrab::Tiling(grab) => TouchGrab::up(grab, data, handle, event), } } @@ -414,25 +407,24 @@ impl TouchGrab for ResizeGrab { handle: &mut TouchInnerHandle<'_, State>, focus: Option<(PointerFocusTarget, Point)>, event: &TouchMotionEvent, - seq: Serial, ) { match self { - ResizeGrab::Floating(grab) => TouchGrab::motion(grab, data, handle, focus, event, seq), - ResizeGrab::Tiling(grab) => TouchGrab::motion(grab, data, handle, focus, event, seq), + ResizeGrab::Floating(grab) => TouchGrab::motion(grab, data, handle, focus, event), + ResizeGrab::Tiling(grab) => TouchGrab::motion(grab, data, handle, focus, event), } } - fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) { + fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) { match self { - ResizeGrab::Floating(grab) => TouchGrab::frame(grab, data, handle, seq), - ResizeGrab::Tiling(grab) => TouchGrab::frame(grab, data, handle, seq), + ResizeGrab::Floating(grab) => TouchGrab::frame(grab, data, handle), + ResizeGrab::Tiling(grab) => TouchGrab::frame(grab, data, handle), } } - fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) { + fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) { match self { - ResizeGrab::Floating(grab) => TouchGrab::cancel(grab, data, handle, seq), - ResizeGrab::Tiling(grab) => TouchGrab::cancel(grab, data, handle, seq), + ResizeGrab::Floating(grab) => TouchGrab::cancel(grab, data, handle), + ResizeGrab::Tiling(grab) => TouchGrab::cancel(grab, data, handle), } } @@ -441,11 +433,10 @@ impl TouchGrab for ResizeGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &ShapeEvent, - seq: Serial, ) { match self { - ResizeGrab::Floating(grab) => TouchGrab::shape(grab, data, handle, event, seq), - ResizeGrab::Tiling(grab) => TouchGrab::shape(grab, data, handle, event, seq), + ResizeGrab::Floating(grab) => TouchGrab::shape(grab, data, handle, event), + ResizeGrab::Tiling(grab) => TouchGrab::shape(grab, data, handle, event), } } @@ -454,11 +445,10 @@ impl TouchGrab for ResizeGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &OrientationEvent, - seq: Serial, ) { match self { - ResizeGrab::Floating(grab) => TouchGrab::orientation(grab, data, handle, event, seq), - ResizeGrab::Tiling(grab) => TouchGrab::orientation(grab, data, handle, event, seq), + ResizeGrab::Floating(grab) => TouchGrab::orientation(grab, data, handle, event), + ResizeGrab::Tiling(grab) => TouchGrab::orientation(grab, data, handle, event), } } @@ -729,24 +719,17 @@ impl TouchGrab for MoveGrab { handle: &mut TouchInnerHandle<'_, State>, focus: Option<(PointerFocusTarget, Point)>, event: &DownEvent, - seq: Serial, ) { match self { - MoveGrab::Move(grab) => TouchGrab::down(grab, data, handle, focus, event, seq), - MoveGrab::Delayed(grab) => TouchGrab::down(grab, data, handle, focus, event, seq), + MoveGrab::Move(grab) => TouchGrab::down(grab, data, handle, focus, event), + MoveGrab::Delayed(grab) => TouchGrab::down(grab, data, handle, focus, event), } } - fn up( - &mut self, - data: &mut State, - handle: &mut TouchInnerHandle<'_, State>, - event: &UpEvent, - seq: Serial, - ) { + fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) { match self { - MoveGrab::Move(grab) => TouchGrab::up(grab, data, handle, event, seq), - MoveGrab::Delayed(grab) => TouchGrab::up(grab, data, handle, event, seq), + MoveGrab::Move(grab) => TouchGrab::up(grab, data, handle, event), + MoveGrab::Delayed(grab) => TouchGrab::up(grab, data, handle, event), } } @@ -756,25 +739,24 @@ impl TouchGrab for MoveGrab { handle: &mut TouchInnerHandle<'_, State>, focus: Option<(PointerFocusTarget, Point)>, event: &TouchMotionEvent, - seq: Serial, ) { match self { - MoveGrab::Move(grab) => TouchGrab::motion(grab, data, handle, focus, event, seq), - MoveGrab::Delayed(grab) => TouchGrab::motion(grab, data, handle, focus, event, seq), + MoveGrab::Move(grab) => TouchGrab::motion(grab, data, handle, focus, event), + MoveGrab::Delayed(grab) => TouchGrab::motion(grab, data, handle, focus, event), } } - fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) { + fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) { match self { - MoveGrab::Move(grab) => TouchGrab::frame(grab, data, handle, seq), - MoveGrab::Delayed(grab) => TouchGrab::frame(grab, data, handle, seq), + MoveGrab::Move(grab) => TouchGrab::frame(grab, data, handle), + MoveGrab::Delayed(grab) => TouchGrab::frame(grab, data, handle), } } - fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) { + fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) { match self { - MoveGrab::Move(grab) => TouchGrab::cancel(grab, data, handle, seq), - MoveGrab::Delayed(grab) => TouchGrab::cancel(grab, data, handle, seq), + MoveGrab::Move(grab) => TouchGrab::cancel(grab, data, handle), + MoveGrab::Delayed(grab) => TouchGrab::cancel(grab, data, handle), } } @@ -783,11 +765,10 @@ impl TouchGrab for MoveGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &ShapeEvent, - seq: Serial, ) { match self { - MoveGrab::Move(grab) => TouchGrab::shape(grab, data, handle, event, seq), - MoveGrab::Delayed(grab) => TouchGrab::shape(grab, data, handle, event, seq), + MoveGrab::Move(grab) => TouchGrab::shape(grab, data, handle, event), + MoveGrab::Delayed(grab) => TouchGrab::shape(grab, data, handle, event), } } @@ -796,11 +777,10 @@ impl TouchGrab for MoveGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &OrientationEvent, - seq: Serial, ) { match self { - MoveGrab::Move(grab) => TouchGrab::orientation(grab, data, handle, event, seq), - MoveGrab::Delayed(grab) => TouchGrab::orientation(grab, data, handle, event, seq), + MoveGrab::Move(grab) => TouchGrab::orientation(grab, data, handle, event), + MoveGrab::Delayed(grab) => TouchGrab::orientation(grab, data, handle, event), } } diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index c92c5bf4..e73b108d 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -40,7 +40,7 @@ use smithay::{ touch::{self, GrabStartData as TouchGrabStartData, TouchGrab, TouchInnerHandle}, }, output::Output, - utils::{IsAlive, Logical, Point, Rectangle, SERIAL_COUNTER, Scale, Serial}, + utils::{IsAlive, Logical, Point, Rectangle, SERIAL_COUNTER, Scale}, }; use std::{ collections::HashSet, @@ -652,9 +652,8 @@ impl TouchGrab for MoveGrab { handle: &mut TouchInnerHandle<'_, State>, _focus: Option<(PointerFocusTarget, Point)>, event: &touch::DownEvent, - seq: Serial, ) { - handle.down(data, None, event, seq) + handle.down(data, None, event) } fn up( @@ -662,13 +661,12 @@ impl TouchGrab for MoveGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &touch::UpEvent, - seq: Serial, ) { if event.slot == >::start_data(self).slot { handle.unset_grab(self, data); } - handle.up(data, event, seq); + handle.up(data, event); } fn motion( @@ -677,20 +675,19 @@ impl TouchGrab for MoveGrab { handle: &mut TouchInnerHandle<'_, State>, _focus: Option<(PointerFocusTarget, Point)>, event: &touch::MotionEvent, - seq: Serial, ) { if event.slot == >::start_data(self).slot { self.update_location(data, event.location); } - handle.motion(data, None, event, seq); + handle.motion(data, None, event); } - fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) { - handle.frame(data, seq) + fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) { + handle.frame(data) } - fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, _seq: Serial) { + fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) { handle.unset_grab(self, data); } @@ -699,9 +696,8 @@ impl TouchGrab for MoveGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &touch::ShapeEvent, - seq: Serial, ) { - handle.shape(data, event, seq) + handle.shape(data, event) } fn orientation( @@ -709,9 +705,8 @@ impl TouchGrab for MoveGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &touch::OrientationEvent, - seq: Serial, ) { - handle.orientation(data, event, seq) + handle.orientation(data, event) } fn start_data(&self) -> &TouchGrabStartData { diff --git a/src/shell/layout/floating/grabs/resize.rs b/src/shell/layout/floating/grabs/resize.rs index a1e63eac..f9e5cbfd 100644 --- a/src/shell/layout/floating/grabs/resize.rs +++ b/src/shell/layout/floating/grabs/resize.rs @@ -29,7 +29,7 @@ use smithay::{ }, }, output::Output, - utils::{IsAlive, Logical, Point, Rectangle, Serial, Size}, + utils::{IsAlive, Logical, Point, Rectangle, Size}, }; use tracing::debug; @@ -330,23 +330,16 @@ impl TouchGrab for ResizeSurfaceGrab { handle: &mut TouchInnerHandle<'_, State>, _focus: Option<(PointerFocusTarget, Point)>, event: &DownEvent, - seq: Serial, ) { - handle.down(data, None, event, seq) + handle.down(data, None, event) } - fn up( - &mut self, - data: &mut State, - handle: &mut TouchInnerHandle<'_, State>, - event: &UpEvent, - seq: Serial, - ) { + fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) { if event.slot == >::start_data(self).slot { handle.unset_grab(self, data); } - handle.up(data, event, seq); + handle.up(data, event); } fn motion( @@ -355,7 +348,6 @@ impl TouchGrab for ResizeSurfaceGrab { handle: &mut TouchInnerHandle<'_, State>, _focus: Option<(PointerFocusTarget, Point)>, event: &TouchMotionEvent, - seq: Serial, ) { if event.slot == >::start_data(self).slot && self.update_location(event.location.as_global()) @@ -363,14 +355,14 @@ impl TouchGrab for ResizeSurfaceGrab { handle.unset_grab(self, data); } - handle.motion(data, None, event, seq); + handle.motion(data, None, event); } - fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) { - handle.frame(data, seq) + fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) { + handle.frame(data) } - fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, _seq: Serial) { + fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) { handle.unset_grab(self, data); } @@ -379,9 +371,8 @@ impl TouchGrab for ResizeSurfaceGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &ShapeEvent, - seq: Serial, ) { - handle.shape(data, event, seq) + handle.shape(data, event) } fn orientation( @@ -389,9 +380,8 @@ impl TouchGrab for ResizeSurfaceGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &OrientationEvent, - seq: Serial, ) { - handle.orientation(data, event, seq) + handle.orientation(data, event) } fn start_data(&self) -> &TouchGrabStartData { diff --git a/src/shell/layout/mod.rs b/src/shell/layout/mod.rs index a2475411..987f2f90 100644 --- a/src/shell/layout/mod.rs +++ b/src/shell/layout/mod.rs @@ -32,7 +32,7 @@ pub fn is_dialog(window: &CosmicSurface) -> bool { } WindowSurface::X11(surface) => { if surface.is_override_redirect() - || surface.is_popup() + || surface.is_modal() || !matches!( surface.window_type(), None | Some(WmWindowType::Normal) | Some(WmWindowType::Utility) diff --git a/src/shell/layout/tiling/grabs/resize.rs b/src/shell/layout/tiling/grabs/resize.rs index 51d5c9a2..9b135119 100644 --- a/src/shell/layout/tiling/grabs/resize.rs +++ b/src/shell/layout/tiling/grabs/resize.rs @@ -22,12 +22,13 @@ use smithay::{ PointerTarget, RelativeMotionEvent, }, touch::{ - DownEvent, GrabStartData as TouchGrabStartData, MotionEvent as TouchMotionEvent, - OrientationEvent, ShapeEvent, TouchGrab, TouchInnerHandle, TouchTarget, UpEvent, + DownEvent, FrameMarker, GrabStartData as TouchGrabStartData, + MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchGrab, + TouchInnerHandle, TouchTarget, UpEvent, }, }, output::WeakOutput, - utils::{IsAlive, Logical, Point, Serial}, + utils::{IsAlive, Logical, Point}, }; use super::super::{Data, TilingLayout}; @@ -126,7 +127,7 @@ impl PointerTarget for ResizeForkTarget { } impl TouchTarget for ResizeForkTarget { - fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent, _seq: Serial) { + fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent) { let seat = seat.clone(); let node = self.node.clone(); let output = self.output.clone(); @@ -157,25 +158,14 @@ impl TouchTarget for ResizeForkTarget { }); } - fn up(&self, _seat: &Seat, _data: &mut State, _event: &UpEvent, _seq: Serial) {} - fn motion( - &self, - _seat: &Seat, - _data: &mut State, - _event: &TouchMotionEvent, - _seq: Serial, - ) { - } - fn frame(&self, _seat: &Seat, _data: &mut State, _seq: Serial) {} - fn cancel(&self, _seat: &Seat, _data: &mut State, _seq: Serial) {} - fn shape(&self, _seat: &Seat, _data: &mut State, _event: &ShapeEvent, _seq: Serial) {} - fn orientation( - &self, - _seat: &Seat, - _data: &mut State, - _event: &OrientationEvent, - _seq: Serial, - ) { + fn up(&self, _seat: &Seat, _data: &mut State, _event: &UpEvent) {} + fn motion(&self, _seat: &Seat, _data: &mut State, _event: &TouchMotionEvent) {} + fn frame(&self, _seat: &Seat, _data: &mut State, _frame: FrameMarker) {} + fn cancel(&self, _seat: &Seat, _data: &mut State, _frame: FrameMarker) {} + fn shape(&self, _seat: &Seat, _data: &mut State, _event: &ShapeEvent) {} + fn orientation(&self, _seat: &Seat, _data: &mut State, _event: &OrientationEvent) {} + fn last_frame(&self, _seat: &Seat, _data: &mut State) -> Option { + None } } @@ -508,23 +498,16 @@ impl TouchGrab for ResizeForkGrab { handle: &mut TouchInnerHandle<'_, State>, _focus: Option<(PointerFocusTarget, Point)>, event: &DownEvent, - seq: Serial, ) { - handle.down(data, None, event, seq) + handle.down(data, None, event) } - fn up( - &mut self, - data: &mut State, - handle: &mut TouchInnerHandle<'_, State>, - event: &UpEvent, - seq: Serial, - ) { + fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) { if event.slot == >::start_data(self).slot { handle.unset_grab(self, data); } - handle.up(data, event, seq); + handle.up(data, event); } fn motion( @@ -533,7 +516,6 @@ impl TouchGrab for ResizeForkGrab { handle: &mut TouchInnerHandle<'_, State>, _focus: Option<(PointerFocusTarget, Point)>, event: &TouchMotionEvent, - seq: Serial, ) { if event.slot == >::start_data(self).slot && self.update_location(data, event.location, false) @@ -541,14 +523,14 @@ impl TouchGrab for ResizeForkGrab { handle.unset_grab(self, data); } - handle.motion(data, None, event, seq); + handle.motion(data, None, event); } - fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) { - handle.frame(data, seq) + fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) { + handle.frame(data) } - fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, _seq: Serial) { + fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) { handle.unset_grab(self, data); } @@ -557,9 +539,8 @@ impl TouchGrab for ResizeForkGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &ShapeEvent, - seq: Serial, ) { - handle.shape(data, event, seq) + handle.shape(data, event) } fn start_data(&self) -> &TouchGrabStartData { @@ -574,9 +555,8 @@ impl TouchGrab for ResizeForkGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &OrientationEvent, - seq: Serial, ) { - handle.orientation(data, event, seq) + handle.orientation(data, event) } fn unset(&mut self, data: &mut State) { diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index b4ba339f..40f8da8e 100644 --- a/src/shell/zoom.rs +++ b/src/shell/zoom.rs @@ -22,8 +22,8 @@ use smithay::{ MotionEvent as PointerMotionEvent, PointerTarget, RelativeMotionEvent, }, touch::{ - DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget, - UpEvent, + DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, + TouchTarget, UpEvent, }, }, output::Output, @@ -1036,58 +1036,59 @@ impl PointerTarget for ZoomFocusTarget { } impl TouchTarget for ZoomFocusTarget { - fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent, seq: Serial) { + fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent) { match self { - ZoomFocusTarget::Main(elem) => TouchTarget::down(elem, seat, data, event, seq), - ZoomFocusTarget::Menu(elem) => TouchTarget::down(elem, seat, data, event, seq), + ZoomFocusTarget::Main(elem) => TouchTarget::down(elem, seat, data, event), + ZoomFocusTarget::Menu(elem) => TouchTarget::down(elem, seat, data, event), } } - fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent, seq: Serial) { + fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent) { match self { - ZoomFocusTarget::Main(elem) => TouchTarget::up(elem, seat, data, event, seq), - ZoomFocusTarget::Menu(elem) => TouchTarget::up(elem, seat, data, event, seq), + ZoomFocusTarget::Main(elem) => TouchTarget::up(elem, seat, data, event), + ZoomFocusTarget::Menu(elem) => TouchTarget::up(elem, seat, data, event), } } - fn motion(&self, seat: &Seat, data: &mut State, event: &TouchMotionEvent, seq: Serial) { + fn motion(&self, seat: &Seat, data: &mut State, event: &TouchMotionEvent) { match self { - ZoomFocusTarget::Main(elem) => TouchTarget::motion(elem, seat, data, event, seq), - ZoomFocusTarget::Menu(elem) => TouchTarget::motion(elem, seat, data, event, seq), + ZoomFocusTarget::Main(elem) => TouchTarget::motion(elem, seat, data, event), + ZoomFocusTarget::Menu(elem) => TouchTarget::motion(elem, seat, data, event), } } - fn frame(&self, seat: &Seat, data: &mut State, seq: Serial) { + fn frame(&self, seat: &Seat, data: &mut State, frame: FrameMarker) { match self { - ZoomFocusTarget::Main(elem) => TouchTarget::frame(elem, seat, data, seq), - ZoomFocusTarget::Menu(elem) => TouchTarget::frame(elem, seat, data, seq), + ZoomFocusTarget::Main(elem) => TouchTarget::frame(elem, seat, data, frame), + ZoomFocusTarget::Menu(elem) => TouchTarget::frame(elem, seat, data, frame), } } - fn cancel(&self, seat: &Seat, data: &mut State, seq: Serial) { + fn cancel(&self, seat: &Seat, data: &mut State, frame: FrameMarker) { match self { - ZoomFocusTarget::Main(elem) => TouchTarget::cancel(elem, seat, data, seq), - ZoomFocusTarget::Menu(elem) => TouchTarget::cancel(elem, seat, data, seq), + ZoomFocusTarget::Main(elem) => TouchTarget::cancel(elem, seat, data, frame), + ZoomFocusTarget::Menu(elem) => TouchTarget::cancel(elem, seat, data, frame), } } - fn shape(&self, seat: &Seat, data: &mut State, event: &ShapeEvent, seq: Serial) { + fn shape(&self, seat: &Seat, data: &mut State, event: &ShapeEvent) { match self { - ZoomFocusTarget::Main(elem) => TouchTarget::shape(elem, seat, data, event, seq), - ZoomFocusTarget::Menu(elem) => TouchTarget::shape(elem, seat, data, event, seq), + ZoomFocusTarget::Main(elem) => TouchTarget::shape(elem, seat, data, event), + ZoomFocusTarget::Menu(elem) => TouchTarget::shape(elem, seat, data, event), } } - fn orientation( - &self, - seat: &Seat, - data: &mut State, - event: &OrientationEvent, - seq: Serial, - ) { + fn orientation(&self, seat: &Seat, data: &mut State, event: &OrientationEvent) { match self { - ZoomFocusTarget::Main(elem) => TouchTarget::orientation(elem, seat, data, event, seq), - ZoomFocusTarget::Menu(elem) => TouchTarget::orientation(elem, seat, data, event, seq), + ZoomFocusTarget::Main(elem) => TouchTarget::orientation(elem, seat, data, event), + ZoomFocusTarget::Menu(elem) => TouchTarget::orientation(elem, seat, data, event), + } + } + + fn last_frame(&self, seat: &Seat, data: &mut State) -> Option { + match self { + ZoomFocusTarget::Main(elem) => TouchTarget::last_frame(elem, seat, data), + ZoomFocusTarget::Menu(elem) => TouchTarget::last_frame(elem, seat, data), } } } diff --git a/src/utils/iced/mod.rs b/src/utils/iced/mod.rs index 186abf04..44a722db 100644 --- a/src/utils/iced/mod.rs +++ b/src/utils/iced/mod.rs @@ -51,13 +51,12 @@ use smithay::{ PointerTarget, RelativeMotionEvent, }, touch::{ - DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget, - UpEvent, + DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, + TouchTarget, UpEvent, }, }, output::Output, - reexports::calloop::RegistrationToken, - reexports::calloop::{self, LoopHandle, futures::Scheduler}, + reexports::calloop::{self, LoopHandle, RegistrationToken, futures::Scheduler}, utils::{ Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, Transform, @@ -180,6 +179,8 @@ pub(crate) struct IcedElementInternal { last_seat: Arc, Serial)>>>, cursor_pos: Option>, touch_map: HashMap, + last_touch_frame: Option, + last_touch_serial: Option, // iced theme: Theme, @@ -228,6 +229,8 @@ impl Clone for IcedElementInternal

{ last_seat: self.last_seat.clone(), cursor_pos: self.cursor_pos, touch_map: self.touch_map.clone(), + last_touch_frame: None, + last_touch_serial: None, theme: self.theme.clone(), renderer, state, @@ -253,6 +256,8 @@ impl fmt::Debug for IcedElementInternal

{ .field("last_seat", &self.last_seat) .field("cursor_pos", &self.cursor_pos) .field("touch_map", &self.touch_map) + .field("last_touch_frame", &self.last_touch_frame) + .field("last_touch_serial", &self.last_touch_serial) .field("theme", &"...") .field("renderer", &"...") .field("state", &"...") @@ -309,6 +314,8 @@ impl IcedElement

{ cursor_pos: None, last_seat, touch_map: HashMap::new(), + last_touch_frame: None, + last_touch_serial: None, theme, renderer, state, @@ -633,7 +640,6 @@ impl TouchTarget for IcedEleme seat: &Seat, _data: &mut crate::state::State, event: &DownEvent, - seq: Serial, ) { let mut internal = self.0.lock().unwrap(); let id = Finger(i32::from(event.slot) as u64); @@ -644,7 +650,8 @@ impl TouchTarget for IcedEleme .queue_event(Event::Touch(TouchEvent::FingerPressed { id, position })); internal.touch_map.insert(id, position); internal.cursor_pos = Some(event_location); - *internal.last_seat.lock().unwrap() = Some((seat.clone(), seq)); + internal.last_touch_serial = Some(event.serial); + *internal.last_seat.lock().unwrap() = Some((seat.clone(), event.serial)); internal.update(false); } @@ -653,12 +660,12 @@ impl TouchTarget for IcedEleme seat: &Seat, _data: &mut crate::state::State, event: &UpEvent, - seq: Serial, ) { let mut internal = self.0.lock().unwrap(); let id = Finger(i32::from(event.slot) as u64); if let Some(position) = internal.touch_map.remove(&id) { - *internal.last_seat.lock().unwrap() = Some((seat.clone(), seq)); + *internal.last_seat.lock().unwrap() = + Some((seat.clone(), internal.last_touch_serial.unwrap())); internal .state .queue_event(Event::Touch(TouchEvent::FingerLifted { id, position })); @@ -671,13 +678,13 @@ impl TouchTarget for IcedEleme seat: &Seat, _data: &mut crate::state::State, event: &TouchMotionEvent, - seq: Serial, ) { let mut internal = self.0.lock().unwrap(); let id = Finger(i32::from(event.slot) as u64); let event_location = event.location.downscale(internal.additional_scale); let position = IcedPoint::new(event_location.x as f32, event_location.y as f32); - *internal.last_seat.lock().unwrap() = Some((seat.clone(), seq)); + *internal.last_seat.lock().unwrap() = + Some((seat.clone(), internal.last_touch_serial.unwrap())); internal .state .queue_event(Event::Touch(TouchEvent::FingerMoved { id, position })); @@ -690,17 +697,19 @@ impl TouchTarget for IcedEleme &self, _seat: &Seat, _data: &mut crate::state::State, - _seq: Serial, + frame: FrameMarker, ) { + self.0.lock().unwrap().last_touch_frame = Some(frame); } fn cancel( &self, _seat: &Seat, _data: &mut crate::state::State, - _seq: Serial, + frame: FrameMarker, ) { let mut internal = self.0.lock().unwrap(); + internal.last_touch_frame = Some(frame); for (id, position) in std::mem::take(&mut internal.touch_map) { internal .state @@ -714,7 +723,6 @@ impl TouchTarget for IcedEleme _seat: &Seat, _data: &mut crate::state::State, _event: &ShapeEvent, - _seq: Serial, ) { } @@ -723,9 +731,16 @@ impl TouchTarget for IcedEleme _seat: &Seat, _data: &mut crate::state::State, _event: &OrientationEvent, - _seq: Serial, ) { } + + fn last_frame( + &self, + _seat: &Seat, + _data: &mut crate::state::State, + ) -> Option { + self.0.lock().unwrap().last_touch_frame + } } impl KeyboardTarget for IcedElement

{ diff --git a/src/xwayland.rs b/src/xwayland.rs index eb3e1570..aba65ef5 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -115,6 +115,7 @@ impl State { &self.common.display_handle, None, std::iter::empty::<(OsString, OsString)>(), + std::iter::empty::(), true, Stdio::null(), Stdio::null(), From bb584aab7f8edc428b3328fb4e1be3869ecdc8e8 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Mon, 29 Jun 2026 17:31:44 +0200 Subject: [PATCH 52/64] backend: Don't attempt dma-copies from newer intel chips --- src/backend/kms/render/gles.rs | 64 +++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/src/backend/kms/render/gles.rs b/src/backend/kms/render/gles.rs index 5d79b1ee..3eb1e599 100644 --- a/src/backend/kms/render/gles.rs +++ b/src/backend/kms/render/gles.rs @@ -1,21 +1,30 @@ // SPDX-License-Identifier: GPL-3.0-only -use smithay::backend::{ - SwapBuffersError, - allocator::{ - Allocator, - dmabuf::{AnyError, Dmabuf, DmabufAllocator}, - gbm::GbmAllocator, - }, - drm::{CreateDrmNodeError, DrmNode}, - renderer::{ - RendererSuper, - gles::{GlesError, GlesRenderer}, - glow::GlowRenderer, - multigpu::{ApiDevice, Error as MultiError, GraphicsApi}, +use clap_lex::OsStrExt; +use rustix::path::Arg; +use smithay::{ + backend::{ + SwapBuffersError, + allocator::{ + Allocator, + dmabuf::{AnyError, Dmabuf, DmabufAllocator}, + gbm::GbmAllocator, + }, + drm::{CreateDrmNodeError, DrmNode}, + renderer::{ + RendererSuper, + gles::{GlesError, GlesRenderer, ffi}, + glow::GlowRenderer, + multigpu::{ApiDevice, Error as MultiError, GraphicsApi}, + }, }, + reexports::drm::control::Device, +}; +use std::{ + borrow::{Borrow, BorrowMut}, + cell::Cell, + ffi::{CStr, c_char}, }; -use std::{borrow::Borrow, cell::Cell}; use std::{ collections::HashMap, fmt, @@ -98,7 +107,7 @@ impl GbmGlowBackend { } } -impl GraphicsApi for GbmGlowBackend { +impl GraphicsApi for GbmGlowBackend { type Device = GbmGlowDevice; type Error = Error; @@ -123,11 +132,26 @@ impl GraphicsApi for GbmGlowBackend { .any(|renderer| renderer.node.dev_id() == node.dev_id()) }) .flat_map(|(node, (allocator, renderer))| { - let renderer = renderer.replace(None)?; + let mut renderer = renderer.replace(None)?; + let is_intel = allocator + .as_ref() + .get_driver() + .is_ok_and(|drv| drv.name().contains("i915")); + let intel_export_quirk = is_intel + && BorrowMut::::borrow_mut(&mut renderer) + .with_context(|gl| unsafe { + CStr::from_ptr(gl.GetString(ffi::RENDERER) as *const c_char) + }) + .is_ok_and(|name| { + name.as_str().is_ok_and(|name| { + name.contains("MTL") || name.contains("ARL") || name.contains("LNL") + }) + }); Some(GbmGlowDevice { node: *node, renderer, + intel_export_quirk, allocator: Box::new(DmabufAllocator(allocator.clone())), }) }) @@ -150,6 +174,7 @@ impl GraphicsApi for GbmGlowBackend { pub struct GbmGlowDevice { node: DrmNode, renderer: GlowRenderer, + intel_export_quirk: bool, allocator: Box>, } @@ -180,9 +205,14 @@ impl ApiDevice for GbmGlowDevice { fn can_do_cross_device_imports(&self) -> bool { !Borrow::::borrow(&self.renderer).is_software() } + + fn should_do_cross_device_exports(&self) -> bool { + !self.intel_export_quirk + } } -impl FromGlesError for MultiError, T> +impl FromGlesError + for MultiError, T> where T::Error: 'static, <::Renderer as RendererSuper>::Error: 'static, From c5775d20109ce5b7785b271d9283636da87c2e52 Mon Sep 17 00:00:00 2001 From: Tom Grushka Date: Tue, 30 Jun 2026 14:21:02 -0600 Subject: [PATCH 53/64] change update_focal_point to use f64 to avoid rounding errors --- src/shell/zoom.rs | 49 ++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index 40f8da8e..ffeed63b 100644 --- a/src/shell/zoom.rs +++ b/src/shell/zoom.rs @@ -259,10 +259,8 @@ impl ZoomState { original_position: Point, movement: ZoomMovement, ) { - let cursor_position = cursor_position.to_i32_round(); - let original_position = original_position.to_i32_round(); - let output_geometry = output.geometry(); - let mut zoomed_output_geometry = output.zoomed_geometry().unwrap(); + let output_geometry = output.geometry().to_f64(); + let mut zoomed_output_geometry = output.zoomed_geometry().unwrap().to_f64(); let output_state = output.user_data().get::>().unwrap(); let mut output_state_ref = output_state.lock().unwrap(); @@ -275,44 +273,40 @@ impl ZoomState { let cursor_position = cursor_position.to_local(output); match movement { - ZoomMovement::Continuously => output_state_ref.focal_point = cursor_position.to_f64(), + ZoomMovement::Continuously => output_state_ref.focal_point = cursor_position, ZoomMovement::OnEdge => { if !zoomed_output_geometry - .overlaps_or_touches(Rectangle::new(original_position, Size::from((16, 16)))) + .overlaps_or_touches(Rectangle::new(original_position, Size::from((16., 16.)))) { zoomed_output_geometry.loc = cursor_position.to_global(output) - - zoomed_output_geometry.size.downscale(2).to_point(); + - zoomed_output_geometry.size.downscale(2.).to_point(); let mut focal_point = zoomed_output_geometry .loc .to_local(output) - .upscale( - output_geometry.size.w - / (output_geometry.size.w - zoomed_output_geometry.size.w), - ) + .upscale(output_state_ref.level) .to_global(output); focal_point.x = focal_point.x.clamp( output_geometry.loc.x, - output_geometry.loc.x + output_geometry.size.w - 1, + output_geometry.loc.x + output_geometry.size.w - 1., ); focal_point.y = focal_point.y.clamp( output_geometry.loc.y, - output_geometry.loc.y + output_geometry.size.h - 1, + output_geometry.loc.y + output_geometry.size.h - 1., ); output_state_ref.previous_point = Some((output_state_ref.focal_point, Instant::now())); - output_state_ref.focal_point = focal_point.to_local(output).to_f64(); + output_state_ref.focal_point = focal_point.to_local(output); } else if !zoomed_output_geometry.contains(cursor_position.to_global(output)) { let mut diff = output_state_ref.focal_point.to_global(output) + (cursor_position.to_global(output) - original_position) - .to_f64() .upscale(output_state_ref.level); diff.x = diff.x.clamp( - output_geometry.loc.x as f64, - ((output_geometry.loc.x + output_geometry.size.w) as f64).next_down(), + output_geometry.loc.x, + (output_geometry.loc.x + output_geometry.size.w).next_down(), ); diff.y = diff.y.clamp( - output_geometry.loc.y as f64, - ((output_geometry.loc.y + output_geometry.size.h) as f64).next_down(), + output_geometry.loc.y, + (output_geometry.loc.y + output_geometry.size.h).next_down(), ); diff -= output_state_ref.focal_point.to_global(output); @@ -321,28 +315,27 @@ impl ZoomState { } ZoomMovement::Centered => { zoomed_output_geometry.loc = cursor_position.to_global(output) - - zoomed_output_geometry.size.downscale(2).to_point(); + - zoomed_output_geometry.size.downscale(2.).to_point(); let mut focal_point = zoomed_output_geometry .loc .to_local(output) .upscale( - output_geometry - .size - .w - .checked_div(output_geometry.size.w - zoomed_output_geometry.size.w) - .unwrap_or(1), + (output_geometry.size.w + / (output_geometry.size.w - zoomed_output_geometry.size.w) + .max(f64::EPSILON)) + .max(1.), ) .to_global(output); focal_point.x = focal_point.x.clamp( output_geometry.loc.x, - output_geometry.loc.x + output_geometry.size.w - 1, + output_geometry.loc.x + output_geometry.size.w - 1., ); focal_point.y = focal_point.y.clamp( output_geometry.loc.y, - output_geometry.loc.y + output_geometry.size.h - 1, + output_geometry.loc.y + output_geometry.size.h - 1., ); - output_state_ref.focal_point = focal_point.to_local(output).to_f64(); + output_state_ref.focal_point = focal_point.to_local(output); } } } From dacdc3db39bd2b0fb1b0b5104686a270f9cac7e4 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 1 Jul 2026 12:06:30 -0400 Subject: [PATCH 54/64] chore: workaround for Tiger Lake --- src/backend/kms/render/gles.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/backend/kms/render/gles.rs b/src/backend/kms/render/gles.rs index 3eb1e599..4a75c0ab 100644 --- a/src/backend/kms/render/gles.rs +++ b/src/backend/kms/render/gles.rs @@ -144,7 +144,10 @@ impl GraphicsApi for GbmGlowBackend { }) .is_ok_and(|name| { name.as_str().is_ok_and(|name| { - name.contains("MTL") || name.contains("ARL") || name.contains("LNL") + name.contains("TGL") + || name.contains("MTL") + || name.contains("ARL") + || name.contains("LNL") }) }); From a9f2427c00dddbd78ddc3990c84751d6993c3f8d Mon Sep 17 00:00:00 2001 From: Alexander Daichendt Date: Sun, 28 Jun 2026 16:30:53 +0200 Subject: [PATCH 55/64] fix: internal laptop screen stuck on vendor logo with external monitor --- src/backend/kms/drm_helpers.rs | 95 +++++++++++++++++++++++----------- src/backend/kms/mod.rs | 25 +++++++-- 2 files changed, 85 insertions(+), 35 deletions(-) diff --git a/src/backend/kms/drm_helpers.rs b/src/backend/kms/drm_helpers.rs index 6144e4f0..bc80b2b8 100644 --- a/src/backend/kms/drm_helpers.rs +++ b/src/backend/kms/drm_helpers.rs @@ -77,24 +77,6 @@ pub fn display_configuration( let mut req = AtomicModeReq::new(); let plane_handles = device.plane_handles()?; - for conn in connectors - .iter() - .flat_map(|conn| device.get_connector(*conn, false).ok()) - .filter(|conn| { - if let Some(enc) = conn.current_encoder() - && let Ok(enc) = device.get_encoder(enc) - && let Some(crtc) = enc.crtc() - { - return cleanup.contains(&crtc); - } - false - }) - .map(|info| info.handle()) - { - let crtc_id = get_prop(device, conn, "CRTC_ID")?; - req.add_property(conn, crtc_id, property::Value::CRTC(None)); - } - // We cannot just shortcut and use the legacy api for all cleanups because of this. // (Technically a device does not need to be atomic for planes to be used, but nobody does this otherwise.) for plane in plane_handles { @@ -108,7 +90,7 @@ pub fn display_configuration( _ => false, }, )?; - if cleanup.contains(&crtc) || !is_primary { + if !is_primary && !cleanup.contains(&crtc) { let crtc_id = get_prop(device, plane, "CRTC_ID")?; let fb_id = get_prop(device, plane, "FB_ID")?; req.add_property(plane, crtc_id, property::Value::CRTC(None)); @@ -116,27 +98,80 @@ pub fn display_configuration( } } } - - for crtc in cleanup { - let mode_id = get_prop(device, crtc, "MODE_ID")?; - let active = get_prop(device, crtc, "ACTIVE")?; - req.add_property(crtc, active, property::Value::Boolean(false)); - req.add_property(crtc, mode_id, property::Value::Unknown(0)); - } - device.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, req)?; } else { for crtc in res_handles.crtcs() { #[allow(deprecated)] let _ = device.set_cursor(*crtc, Option::<&DumbBuffer>::None); } - for crtc in cleanup { + } + disable_crtcs(device, supports_atomic, &cleanup)?; + + Ok(map) +} + +/// Disables the given CRTCs and detaches their connectors/planes. +pub fn disable_crtcs( + device: &mut impl ControlDevice, + supports_atomic: bool, + crtcs: &[crtc::Handle], +) -> Result<()> { + if crtcs.is_empty() { + return Ok(()); + } + + let res_handles = device.resource_handles()?; + + if supports_atomic { + let mut req = AtomicModeReq::new(); + + for conn in res_handles + .connectors() + .iter() + .flat_map(|conn| device.get_connector(*conn, false).ok()) + .filter(|conn| { + if let Some(enc) = conn.current_encoder() + && let Ok(enc) = device.get_encoder(enc) + && let Some(crtc) = enc.crtc() + { + return crtcs.contains(&crtc); + } + false + }) + .map(|info| info.handle()) + { + let crtc_id = get_prop(device, conn, "CRTC_ID")?; + req.add_property(conn, crtc_id, property::Value::CRTC(None)); + } + + for plane in device.plane_handles()? { + let info = device.get_plane(plane)?; + if let Some(crtc) = info.crtc() + && crtcs.contains(&crtc) + { + let crtc_id = get_prop(device, plane, "CRTC_ID")?; + let fb_id = get_prop(device, plane, "FB_ID")?; + req.add_property(plane, crtc_id, property::Value::CRTC(None)); + req.add_property(plane, fb_id, property::Value::Framebuffer(None)); + } + } + + for crtc in crtcs { + let mode_id = get_prop(device, *crtc, "MODE_ID")?; + let active = get_prop(device, *crtc, "ACTIVE")?; + req.add_property(*crtc, active, property::Value::Boolean(false)); + req.add_property(*crtc, mode_id, property::Value::Unknown(0)); + } + + device.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, req)?; + } else { + for crtc in crtcs { // null commit (necessary to trigger removal on the kernel side with the legacy api.) - let _ = device.set_crtc(crtc, None, (0, 0), &[], None); + let _ = device.set_crtc(*crtc, None, (0, 0), &[], None); } } - Ok(map) + Ok(()) } pub fn interface_name(device: &impl ControlDevice, connector: connector::Handle) -> Result { diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 694d6ce7..07612790 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -852,11 +852,26 @@ impl KmsGuard<'_> { // first drop old surfaces if !test_only { - for output in outputs.iter().filter(|o| !o.is_enabled()) { - device - .inner - .surfaces - .retain(|_, surface| surface.output != *output); + let mut disabled_crtcs = Vec::new(); + device.inner.surfaces.retain(|crtc, surface| { + if outputs + .iter() + .any(|o| !o.is_enabled() && surface.output == *o) + { + disabled_crtcs.push(*crtc); + false + } else { + true + } + }); + + let supports_atomic = device.drm.device().is_atomic(); + if let Err(err) = drm_helpers::disable_crtcs( + device.drm.device_mut(), + supports_atomic, + &disabled_crtcs, + ) { + warn!("Failed to disable crtcs for disabled outputs: {err}"); } } From f5e0ba1594e428954f2a1647b03564be1afaec4a Mon Sep 17 00:00:00 2001 From: Alexander Daichendt Date: Mon, 29 Jun 2026 20:42:23 +0200 Subject: [PATCH 56/64] make device `DrmDevice` in disable_crts --- src/backend/kms/device.rs | 6 +----- src/backend/kms/drm_helpers.rs | 16 ++++++---------- src/backend/kms/mod.rs | 9 +++------ 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/backend/kms/device.rs b/src/backend/kms/device.rs index c7a0a160..2b0e8f9d 100644 --- a/src/backend/kms/device.rs +++ b/src/backend/kms/device.rs @@ -96,7 +96,6 @@ pub struct Device { pub inner: InnerDevice, pub drm: GbmDrmOutputManager, - supports_atomic: bool, pub texture_formats: FormatSet, event_token: Option, pub socket: Option, @@ -711,7 +710,6 @@ impl Device { let (drm, notifier) = DrmDevice::new(fd.clone(), false) .with_context(|| format!("Failed to initialize drm device for: {}", path.display()))?; let dev_node = DrmNode::from_dev_id(dev)?; - let supports_atomic = drm.is_atomic(); let gbm = GbmDevice::new(fd) .with_context(|| format!("Failed to initialize GBM device for {}", path.display()))?; @@ -831,7 +829,6 @@ impl Device { active_clients, }, - supports_atomic, texture_formats, event_token: Some(token), socket, @@ -840,8 +837,7 @@ impl Device { pub fn enumerate_surfaces(&mut self) -> Result { // enumerate our outputs - let config = - drm_helpers::display_configuration(self.drm.device_mut(), self.supports_atomic)?; + let config = drm_helpers::display_configuration(self.drm.device_mut())?; let surfaces = self .inner diff --git a/src/backend/kms/drm_helpers.rs b/src/backend/kms/drm_helpers.rs index bc80b2b8..8629c9d7 100644 --- a/src/backend/kms/drm_helpers.rs +++ b/src/backend/kms/drm_helpers.rs @@ -3,6 +3,7 @@ use anyhow::{Context, Result, anyhow}; use libdisplay_info::{edid::DisplayDescriptorTag, info::Info}; use smithay::{ + backend::drm::DrmDevice, reexports::drm::control::{ AtomicCommitFlags, Device as ControlDevice, Mode, ModeFlags, PlaneType, ResourceHandle, atomic::AtomicModeReq, @@ -16,8 +17,7 @@ use smithay::{ use std::{collections::HashMap, ops::Range}; pub fn display_configuration( - device: &mut impl ControlDevice, - supports_atomic: bool, + device: &mut DrmDevice, ) -> Result>> { let res_handles = device.resource_handles()?; let connectors = res_handles.connectors(); @@ -73,7 +73,7 @@ pub fn display_configuration( } // And then cleanup - if supports_atomic { + if device.is_atomic() { let mut req = AtomicModeReq::new(); let plane_handles = device.plane_handles()?; @@ -105,24 +105,20 @@ pub fn display_configuration( let _ = device.set_cursor(*crtc, Option::<&DumbBuffer>::None); } } - disable_crtcs(device, supports_atomic, &cleanup)?; + disable_crtcs(device, &cleanup)?; Ok(map) } /// Disables the given CRTCs and detaches their connectors/planes. -pub fn disable_crtcs( - device: &mut impl ControlDevice, - supports_atomic: bool, - crtcs: &[crtc::Handle], -) -> Result<()> { +pub fn disable_crtcs(device: &mut DrmDevice, crtcs: &[crtc::Handle]) -> Result<()> { if crtcs.is_empty() { return Ok(()); } let res_handles = device.resource_handles()?; - if supports_atomic { + if device.is_atomic() { let mut req = AtomicModeReq::new(); for conn in res_handles diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 07612790..13832da8 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -865,12 +865,9 @@ impl KmsGuard<'_> { } }); - let supports_atomic = device.drm.device().is_atomic(); - if let Err(err) = drm_helpers::disable_crtcs( - device.drm.device_mut(), - supports_atomic, - &disabled_crtcs, - ) { + if let Err(err) = + drm_helpers::disable_crtcs(device.drm.device_mut(), &disabled_crtcs) + { warn!("Failed to disable crtcs for disabled outputs: {err}"); } } From 9d52653d5c9d45716dbc333f99f5ef7e25549bf8 Mon Sep 17 00:00:00 2001 From: Hojjat Abdollahi Date: Thu, 2 Jul 2026 09:53:03 -0600 Subject: [PATCH 57/64] revert: "fix: follow the focus after alt+tab to another output" This reverts commit 56f84fba2dc0ff7de191e48e56f29d92ba50e668. --- src/wayland/handlers/toplevel_management.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/wayland/handlers/toplevel_management.rs b/src/wayland/handlers/toplevel_management.rs index 2b1232d5..3da39952 100644 --- a/src/wayland/handlers/toplevel_management.rs +++ b/src/wayland/handlers/toplevel_management.rs @@ -111,8 +111,10 @@ impl ToplevelManagementHandler for State { std::mem::drop(shell); // move pointer to window if it’s on a different monitor/output - let switching_output = seat.active_output() != *output; - if switching_output && let Some(new_pos) = new_pos { + if seat.active_output() != *output + && self.common.config.cosmic_conf.cursor_follows_focus + && let Some(new_pos) = new_pos + { seat.set_active_output(output); if let Some(ptr) = seat.get_pointer() { let serial = SERIAL_COUNTER.next_serial(); From a5c0d7a17f637e31b9710607a8992f764eb04a3e Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Sun, 24 May 2026 11:40:51 +0200 Subject: [PATCH 58/64] chore: align compositor with local cosmic stack --- Cargo.toml | 51 ++++++++++++++++++++++++++++------- cosmic-comp-config/Cargo.toml | 4 +-- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f1a04f02..a13c3fb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,18 +17,18 @@ cosmic-comp-config = { path = "cosmic-comp-config", features = [ "libdisplay-info", "output", ] } -cosmic-config = { git = "https://github.com/pop-os/libcosmic", features = [ +cosmic-config = { path = "../libcosmic/cosmic-config", features = [ "calloop", "macro", ] } -cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", rev = "160b086", default-features = false, features = [ +cosmic-protocols = { path = "../cosmic-protocols", default-features = false, features = [ "server", ] } -cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon" } -cosmic-settings-daemon-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", features = [ +cosmic-settings-config = { path = "../cosmic-settings-daemon/config" } +cosmic-settings-daemon-config = { path = "../cosmic-settings-daemon/cosmic-settings-daemon-config", features = [ "greeter", ] } -cosmic-text = { git = "https://github.com/pop-os/cosmic-text.git", features = [ +cosmic-text = { git = "https://forge.aditua.com/leyoda/cosmic-text.git", branch = "local/pr-503", features = [ "shape-run-cache", ] } libdisplay-info = "0.3.0" @@ -39,10 +39,14 @@ i18n-embed = { version = "0.16", features = [ "desktop-requester", ] } i18n-embed-fl = "0.10" -iced_tiny_skia = { git = "https://github.com/pop-os/libcosmic" } +iced_tiny_skia = { path = "../libcosmic/iced/tiny_skia" } indexmap = "2.13" keyframe = "1.1.1" -libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false } +cosmic = { package = "libcosmic-yoda", path = "../libcosmic", default-features = false, features = [ + "tokio", + "wayland", + "multi-window", +] } libsystemd = { version = "0.7", optional = true } log-panics = { version = "2", features = ["with-backtrace"] } ordered-float = "5.1" @@ -138,9 +142,36 @@ inherits = "release" [profile.release] lto = "fat" -[patch."https://github.com/pop-os/cosmic-protocols"] -cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } -cosmic-client-toolkit = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } +[patch.'https://github.com/pop-os/libcosmic'] +cosmic-config = { path = "../libcosmic/cosmic-config" } +cosmic-theme = { path = "../libcosmic/cosmic-theme" } +iced = { path = "../libcosmic/iced" } +iced_accessibility = { path = "../libcosmic/iced/accessibility" } +iced_core = { path = "../libcosmic/iced/core" } +iced_futures = { path = "../libcosmic/iced/futures" } +iced_graphics = { path = "../libcosmic/iced/graphics" } +iced_renderer = { path = "../libcosmic/iced/renderer" } +iced_runtime = { path = "../libcosmic/iced/runtime" } +iced_tiny_skia = { path = "../libcosmic/iced/tiny_skia" } +iced_wgpu = { path = "../libcosmic/iced/wgpu" } +iced_widget = { path = "../libcosmic/iced/widget" } +iced_winit = { path = "../libcosmic/iced/winit" } + +[patch.'https://github.com/pop-os/cosmic-protocols'] +cosmic-protocols = { path = "../cosmic-protocols" } +cosmic-client-toolkit = { path = "../cosmic-protocols/client-toolkit" } + +[patch.'https://github.com/pop-os/cosmic-settings-daemon'] +cosmic-settings-config = { path = "../cosmic-settings-daemon/config" } +cosmic-settings-daemon-config = { path = "../cosmic-settings-daemon/cosmic-settings-daemon-config" } + +[patch.'https://github.com/pop-os/cosmic-text.git'] +cosmic-text = { git = "https://forge.aditua.com/leyoda/cosmic-text.git", branch = "local/pr-503" } + +[patch.'https://forge.aditua.com/leyoda/window_clipboard.git'] +window_clipboard = { path = "../window_clipboard" } +dnd = { path = "../window_clipboard/dnd" } +mime = { path = "../window_clipboard/mime" } [patch.crates-io] smithay = { git = "https://github.com/smithay/smithay.git", rev = "8eb4076" } diff --git a/cosmic-comp-config/Cargo.toml b/cosmic-comp-config/Cargo.toml index f01a4bb3..d0225691 100644 --- a/cosmic-comp-config/Cargo.toml +++ b/cosmic-comp-config/Cargo.toml @@ -4,8 +4,8 @@ version = "1.0.0" edition = "2024" [dependencies] -cosmic-config = { git = "https://github.com/pop-os/libcosmic/" } -cosmic-randr-shell = { git = "https://github.com/pop-os/cosmic-randr/", optional = true } +cosmic-config = { path = "../../libcosmic/cosmic-config" } +cosmic-randr-shell = { path = "../../cosmic-randr/shell", optional = true } input = "0.10.0" libdisplay-info = { version = "0.3.0", optional = true } serde = { version = "1", features = ["derive"] } From a005fddefeb20c7458a486172840b65d60cd7285 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Sun, 24 May 2026 16:08:29 +0200 Subject: [PATCH 59/64] fix: keep tokio runtime available for compositor --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 9 +++++++++ 3 files changed, 11 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 1bc58958..262260b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -845,6 +845,7 @@ dependencies = [ "smithay-egui", "thiserror 2.0.18", "tiny-skia", + "tokio", "tracing", "tracing-journald", "tracing-subscriber", diff --git a/Cargo.toml b/Cargo.toml index a13c3fb9..943c6872 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,6 +77,7 @@ xdg = "^3.0" xdg-user = "0.2.1" xkbcommon = "0.9" zbus = "5.14.0" +tokio = { version = "1", features = ["rt-multi-thread"] } profiling = { version = "1.0" } rustix = { version = "1.1.4", features = ["process"] } rand = "0.10" diff --git a/src/main.rs b/src/main.rs index 7ae05991..ffa37236 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,15 @@ // SPDX-License-Identifier: GPL-3.0-only fn main() { + // libcosmic-yoda enables zbus' `tokio` feature, so zbus 5.14 expects an + // ambient Tokio runtime via Handle::current(). cosmic-comp's loop is not + // async, so hold a runtime guard for the lifetime of run(). + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .expect("failed to build tokio runtime"); + let _guard = runtime.enter(); + if let Err(err) = cosmic_comp::run(Default::default()) { tracing::error!("Error occured in main(): {}", err); std::process::exit(1); From 6e72e4c1710b2887bdd2c11047e45a830808ce0c Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Mon, 25 May 2026 09:55:23 +0200 Subject: [PATCH 60/64] yoda: refresh local dbus binding lock --- Cargo.lock | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 262260b7..7669d8af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -955,8 +955,7 @@ dependencies = [ [[package]] name = "cosmic-settings-daemon" -version = "0.1.0" -source = "git+https://github.com/pop-os/dbus-settings-bindings#507e342c21d3ce6ae41b1d4f3fa2f0ad5ee23e75" +version = "0.1.1-yoda.1" dependencies = [ "zbus", ] From 7d24cd2e6ab929d9ad39df200e481608b2ea0dab Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Mon, 25 May 2026 11:34:38 +0200 Subject: [PATCH 61/64] chore: use local cosmic-text checkout --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 943c6872..277655e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ cosmic-settings-config = { path = "../cosmic-settings-daemon/config" } cosmic-settings-daemon-config = { path = "../cosmic-settings-daemon/cosmic-settings-daemon-config", features = [ "greeter", ] } -cosmic-text = { git = "https://forge.aditua.com/leyoda/cosmic-text.git", branch = "local/pr-503", features = [ +cosmic-text = { path = "../cosmic-text", features = [ "shape-run-cache", ] } libdisplay-info = "0.3.0" @@ -167,7 +167,7 @@ cosmic-settings-config = { path = "../cosmic-settings-daemon/config" } cosmic-settings-daemon-config = { path = "../cosmic-settings-daemon/cosmic-settings-daemon-config" } [patch.'https://github.com/pop-os/cosmic-text.git'] -cosmic-text = { git = "https://forge.aditua.com/leyoda/cosmic-text.git", branch = "local/pr-503" } +cosmic-text = { path = "../cosmic-text" } [patch.'https://forge.aditua.com/leyoda/window_clipboard.git'] window_clipboard = { path = "../window_clipboard" } From 58b0598de8d95298eaea440435649bdd281494f9 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Mon, 25 May 2026 18:28:56 +0200 Subject: [PATCH 62/64] chore: use local glyphon lockfile --- Cargo.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 7669d8af..890b86ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1052,7 +1052,6 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cryoglyph" version = "0.1.0" -source = "git+https://github.com/pop-os/glyphon.git?tag=cosmic-0.14#c49de15bce4d8254ac136d1be9911960cc85ce12" dependencies = [ "cosmic-text", "etagere", From 9141780b3036268e38d4d58345cbe892e951f6d8 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Fri, 3 Jul 2026 12:36:51 +0200 Subject: [PATCH 63/64] fix: absorb libcosmic theme-v2 API (private containers, text::Style, wgpu 28) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Leyoda 2026 – GPLv3 --- src/shell/element/stack.rs | 4 ++-- src/shell/element/stack/tab.rs | 4 ++-- src/shell/element/window.rs | 2 +- src/shell/grabs/menu/mod.rs | 8 +++++--- src/shell/zoom.rs | 2 +- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 9f8584f5..058719e3 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -1364,8 +1364,8 @@ impl Decorations for DefaultDecorations { iced_widget::container::Style { snap: true, - icon_color: Some(cosmic_theme.background.on.into()), - text_color: Some(cosmic_theme.background.on.into()), + icon_color: Some(cosmic_theme.background(false).on.into()), + text_color: Some(cosmic_theme.background(false).on.into()), background: Some(Background::Color(background.into())), border: Border { radius, diff --git a/src/shell/element/stack/tab.rs b/src/shell/element/stack/tab.rs index 99b0c247..ea48a585 100644 --- a/src/shell/element/stack/tab.rs +++ b/src/shell/element/stack/tab.rs @@ -65,7 +65,7 @@ impl From for theme::Container<'_> { icon_color: Some(Color::from(theme.cosmic().accent_text_color())), text_color: Some(Color::from(theme.cosmic().accent_text_color())), background: Some(Background::Color( - theme.cosmic().primary.component.selected.into(), + theme.cosmic().primary(false).component.selected.into(), )), border: Border { radius: 0.0.into(), @@ -81,7 +81,7 @@ impl From for theme::Container<'_> { icon_color: None, text_color: None, background: Some(Background::Color( - theme.cosmic().primary.component.base.into(), + theme.cosmic().primary(false).component.base.into(), )), border: Border { radius: 0.0.into(), diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 0adbfab7..ea20bb6a 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -826,7 +826,7 @@ impl Program for CosmicWindowInternal { fn background_color(&self, theme: &cosmic::Theme) -> Color { if self.window.is_maximized(false) { - theme.cosmic().background.base.into() + theme.cosmic().background(false).base.into() } else { Color::TRANSPARENT } diff --git a/src/shell/grabs/menu/mod.rs b/src/shell/grabs/menu/mod.rs index 14a9f917..671e004a 100644 --- a/src/shell/grabs/menu/mod.rs +++ b/src/shell/grabs/menu/mod.rs @@ -428,10 +428,11 @@ impl Program for ContextMenu { .width(mode) .class(if *disabled { theme::Text::Custom(|theme| { - let mut color = theme.cosmic().background.component.on; + let mut color = theme.cosmic().background(false).component.on; color.alpha *= 0.5; TextStyle { color: Some(color.into()), + ..Default::default() } }) } else { @@ -446,10 +447,11 @@ impl Program for ContextMenu { .align_x(Horizontal::Right) .width(Length::Shrink) .class(theme::Text::Custom(|theme| { - let mut color = theme.cosmic().background.component.on; + let mut color = theme.cosmic().background(false).component.on; color.alpha *= 0.75; TextStyle { color: Some(color.into()), + ..Default::default() } })) .into(), @@ -474,7 +476,7 @@ impl Program for ContextMenu { .padding(1) .class(theme::Container::custom(|theme| { let cosmic = theme.cosmic(); - let component = &cosmic.background.component; + let component = &cosmic.background(false).component; iced_widget::container::Style { snap: true, icon_color: Some(cosmic.accent.base.into()), diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index ffeed63b..f72ad8e8 100644 --- a/src/shell/zoom.rs +++ b/src/shell/zoom.rs @@ -505,7 +505,7 @@ impl Program for ZoomProgram { .padding(8) .class(theme::Container::custom(|theme| { let cosmic = theme.cosmic(); - let component = &cosmic.background.component; + let component = &cosmic.background(false).component; iced_widget::container::Style { snap: true, icon_color: Some(component.on.into()), From e65b97abb8c37efc6d83ada3824c006fdfdcd96e Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Fri, 3 Jul 2026 15:56:20 +0200 Subject: [PATCH 64/64] chore: restore rust-toolchain 1.93 + refresh lockfile post-rebase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Leyoda 2026 – GPLv3 --- Cargo.lock | 1413 ++++++++++++++++++++++++++++++++++++------- rust-toolchain.toml | 2 +- 2 files changed, 1193 insertions(+), 222 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 890b86ff..5edc17e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,89 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" +[[package]] +name = "accesskit" +version = "0.22.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "uuid", +] + +[[package]] +name = "accesskit_atspi_common" +version = "0.15.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "accesskit_consumer", + "atspi-common", + "serde", + "zvariant", +] + +[[package]] +name = "accesskit_consumer" +version = "0.32.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "hashbrown 0.16.1", +] + +[[package]] +name = "accesskit_macos" +version = "0.23.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "accesskit_consumer", + "hashbrown 0.16.1", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "accesskit_unix" +version = "0.18.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "accesskit_atspi_common", + "atspi", + "futures-lite", + "serde", + "tokio", + "tokio-stream", + "zbus", +] + +[[package]] +name = "accesskit_windows" +version = "0.30.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "accesskit_consumer", + "hashbrown 0.16.1", + "static_assertions", + "windows 0.61.3", + "windows-core 0.61.2", +] + +[[package]] +name = "accesskit_winit" +version = "0.30.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "accesskit_macos", + "accesskit_unix", + "accesskit_windows", + "raw-window-handle", + "winit 0.31.0-beta.2", +] + [[package]] name = "addr2line" version = "0.25.1" @@ -61,6 +144,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "almost" version = "0.2.0" @@ -175,6 +264,44 @@ dependencies = [ "libloading", ] +[[package]] +name = "ashpd" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f3f79755c74fd155000314eb349864caa787c6592eace6c6882dad873d9c39" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.9.2", + "raw-window-handle", + "serde", + "serde_repr", + "tokio", + "url", + "zbus", +] + +[[package]] +name = "ashpd" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33a3c86f3fd70c0ffa500ed189abfa90b5a52398a45d5dc372fcc38ebeb7a645" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.9.2", + "serde", + "serde_repr", + "tokio", + "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus", +] + [[package]] name = "async-broadcast" version = "0.7.2" @@ -329,10 +456,47 @@ dependencies = [ ] [[package]] -name = "auto_enums" -version = "0.8.7" +name = "atspi" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c170965892137a3a9aeb000b4524aa3cc022a310e709d848b6e1cdce4ab4781" +checksum = "c77886257be21c9cd89a4ae7e64860c6f0eefca799bb79127913052bd0eefb3d" +dependencies = [ + "atspi-common", + "atspi-proxies", +] + +[[package]] +name = "atspi-common" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20c5617155740c98003016429ad13fe43ce7a77b007479350a9f8bf95a29f63d" +dependencies = [ + "enumflags2", + "serde", + "static_assertions", + "zbus", + "zbus-lockstep", + "zbus-lockstep-macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "atspi-proxies" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2230e48787ed3eb4088996eab66a32ca20c0b67bbd4fd6cdfe79f04f1f04c9fc" +dependencies = [ + "atspi-common", + "serde", + "zbus", +] + +[[package]] +name = "auto_enums" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e4487600931c9a89f8db7ffbdf3fbdd45bb7bd85e26861f659a463cd0dff966" dependencies = [ "derive_utils", "proc-macro2", @@ -358,7 +522,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -427,13 +591,22 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2 0.5.2", +] + [[package]] name = "block2" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" dependencies = [ - "objc2", + "objc2 0.6.4", ] [[package]] @@ -619,7 +792,7 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -640,7 +813,6 @@ dependencies = [ [[package]] name = "clipboard_macos" version = "0.1.0" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "objc", "objc-foundation", @@ -650,22 +822,12 @@ dependencies = [ [[package]] name = "clipboard_wayland" version = "0.2.2" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "dnd", "mime 0.1.0", "smithay-clipboard", ] -[[package]] -name = "clipboard_x11" -version = "0.4.2" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" -dependencies = [ - "thiserror 1.0.69", - "x11rb", -] - [[package]] name = "cocoa" version = "0.25.0" @@ -675,7 +837,7 @@ dependencies = [ "bitflags 1.3.2", "block", "cocoa-foundation", - "core-foundation", + "core-foundation 0.9.4", "core-graphics", "foreign-types", "libc", @@ -690,8 +852,8 @@ checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" dependencies = [ "bitflags 1.3.2", "block", - "core-foundation", - "core-graphics-types", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", "libc", "objc", ] @@ -702,7 +864,9 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ - "unicode-width", + "serde", + "termcolor", + "unicode-width 0.1.14", ] [[package]] @@ -746,6 +910,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -759,8 +933,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", "foreign-types", "libc", ] @@ -772,7 +946,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.10.1", "libc", ] @@ -788,7 +973,6 @@ dependencies = [ [[package]] name = "cosmic-client-toolkit" version = "0.2.0" -source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#160b086abe03cd34a8a375d7fbe47b24308d1f38" dependencies = [ "bitflags 2.11.0", "cosmic-protocols", @@ -824,7 +1008,7 @@ dependencies = [ "indexmap 2.13.0", "jiff", "keyframe", - "libcosmic", + "libcosmic-yoda", "libdisplay-info", "libsystemd", "log-panics", @@ -875,7 +1059,6 @@ dependencies = [ [[package]] name = "cosmic-config" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "atomicwrites", "calloop", @@ -888,6 +1071,7 @@ dependencies = [ "notify", "ron 0.12.0", "serde", + "tokio", "tracing", "xdg", "zbus", @@ -896,7 +1080,6 @@ dependencies = [ [[package]] name = "cosmic-config-derive" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "quote", "syn", @@ -905,12 +1088,11 @@ dependencies = [ [[package]] name = "cosmic-freedesktop-icons" version = "0.4.0" -source = "git+https://github.com/pop-os/freedesktop-icons#7a61a704f6d1ec41f71cbe766e3cc484858523fa" dependencies = [ "bstr", "btoi", "memchr", - "memmap2", + "memmap2 0.9.10", "thiserror 2.0.18", "tracing", "xdg", @@ -919,7 +1101,6 @@ dependencies = [ [[package]] name = "cosmic-protocols" version = "0.2.0" -source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#160b086abe03cd34a8a375d7fbe47b24308d1f38" dependencies = [ "bitflags 2.11.0", "wayland-backend", @@ -933,7 +1114,6 @@ dependencies = [ [[package]] name = "cosmic-randr-shell" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-randr/#6e8e795970fa06d434af22775e415b517f7552d3" dependencies = [ "kdl", "slotmap", @@ -943,7 +1123,6 @@ dependencies = [ [[package]] name = "cosmic-settings-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-settings-daemon#e37160f14d1e7ee428f973cd2848b4e95f83dfe1" dependencies = [ "cosmic-config", "ron 0.11.0", @@ -963,7 +1142,6 @@ dependencies = [ [[package]] name = "cosmic-settings-daemon-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-settings-daemon#e37160f14d1e7ee428f973cd2848b4e95f83dfe1" dependencies = [ "cosmic-config", "cosmic-theme", @@ -972,8 +1150,7 @@ dependencies = [ [[package]] name = "cosmic-text" -version = "0.18.2" -source = "git+https://github.com/pop-os/cosmic-text.git#d5a972a2b63649fad11ea3a7e80f7dc4c592f01a" +version = "0.19.0" dependencies = [ "bitflags 2.11.0", "fontdb", @@ -991,18 +1168,19 @@ dependencies = [ "unicode-linebreak", "unicode-script", "unicode-segmentation", + "unicode-width 0.2.2", ] [[package]] name = "cosmic-theme" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "almost", "configparser", "cosmic-config", "csscolorparser", "dirs", + "hex_color", "palette", "ron 0.12.0", "serde", @@ -1078,9 +1256,9 @@ checksum = "42aaeae719fd78ce501d77c6cdf01f7e96f26bcd5617a4903a1c2b97e388543a" [[package]] name = "csscolorparser" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a878cfd6075649977f548ed439564cfdbe1f190a6656a00b48af7740f2f148" +checksum = "199f851bd3cb5004c09474252c7f74e7c047441ed0979bf3688a7106a13da952" dependencies = [ "num-traits", "phf 0.13.1", @@ -1213,7 +1391,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ "bitflags 2.11.0", - "objc2", + "block2 0.6.2", + "libc", + "objc2 0.6.4", ] [[package]] @@ -1239,7 +1419,6 @@ dependencies = [ [[package]] name = "dnd" version = "0.1.0" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "bitflags 2.11.0", "mime 0.1.0", @@ -1266,21 +1445,12 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dpi" version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] -name = "drm" -version = "0.11.1" +name = "dpi" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde" -dependencies = [ - "bitflags 2.11.0", - "bytemuck", - "drm-ffi 0.7.1", - "drm-fourcc", - "rustix 0.38.44", -] +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] name = "drm" @@ -1290,29 +1460,19 @@ checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f" dependencies = [ "bitflags 2.11.0", "bytemuck", - "drm-ffi 0.9.0", + "drm-ffi", "drm-fourcc", "libc", "rustix 0.38.44", ] -[[package]] -name = "drm-ffi" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41334f8405792483e32ad05fbb9c5680ff4e84491883d2947a4757dc54cb2ac6" -dependencies = [ - "drm-sys 0.6.1", - "rustix 0.38.44", -] - [[package]] name = "drm-ffi" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8e41459d99a9b529845f6d2c909eb9adf3b6d2f82635ae40be8de0601726e8b" dependencies = [ - "drm-sys 0.8.0", + "drm-sys", "rustix 0.38.44", ] @@ -1322,16 +1482,6 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" -[[package]] -name = "drm-sys" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d09ff881f92f118b11105ba5e34ff8f4adf27b30dae8f12e28c193af1c83176" -dependencies = [ - "libc", - "linux-raw-sys 0.6.5", -] - [[package]] name = "drm-sys" version = "0.8.0" @@ -1731,7 +1881,7 @@ checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" dependencies = [ "fontconfig-parser", "log", - "memmap2", + "memmap2 0.9.10", "slotmap", "tinyvec", "ttf-parser", @@ -1890,7 +2040,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce852e998d3ca5e4a97014fb31c940dc5ef344ec7d364984525fd11e8a547e6a" dependencies = [ "bitflags 2.11.0", - "drm 0.14.1", + "drm", "drm-fourcc", "gbm-sys", "libc", @@ -1916,8 +2066,8 @@ dependencies = [ "libc", "log", "rustversion", - "windows-link", - "windows-result", + "windows-link 0.2.1", + "windows-result 0.4.1", ] [[package]] @@ -1937,7 +2087,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ "rustix 1.1.4", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -2022,6 +2172,49 @@ dependencies = [ "web-sys", ] +[[package]] +name = "glutin_wgl_sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu-allocator" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" +dependencies = [ + "ash", + "hashbrown 0.16.1", + "log", + "presser", + "thiserror 2.0.18", + "windows 0.62.2", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" +dependencies = [ + "bitflags 2.11.0", + "gpu-descriptor-types", + "hashbrown 0.15.5", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.11.0", +] + [[package]] name = "grid" version = "1.0.0" @@ -2084,6 +2277,8 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.2.0", ] @@ -2117,6 +2312,17 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex_color" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d37f101bf4c633f7ca2e4b5e136050314503dd198e78e325ea602c327c484ef0" +dependencies = [ + "arrayvec", + "rand 0.8.5", + "serde", +] + [[package]] name = "hexf-parse" version = "0.2.1" @@ -2220,7 +2426,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.62.2", ] [[package]] @@ -2235,9 +2441,9 @@ dependencies = [ [[package]] name = "iced" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "dnd", + "iced_accessibility", "iced_core", "iced_debug", "iced_futures", @@ -2245,19 +2451,28 @@ dependencies = [ "iced_renderer", "iced_runtime", "iced_widget", + "iced_winit", "image", "mime 0.1.0", "thiserror 2.0.18", "window_clipboard", ] +[[package]] +name = "iced_accessibility" +version = "0.1.0" +dependencies = [ + "accesskit", + "accesskit_winit", +] + [[package]] name = "iced_core" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "bitflags 2.11.0", "bytes", + "cosmic-client-toolkit", "dnd", "glam", "lilt", @@ -2270,6 +2485,7 @@ dependencies = [ "serde", "smol_str", "thiserror 2.0.18", + "unicode-segmentation", "web-time", "window_clipboard", ] @@ -2277,7 +2493,6 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "iced_core", "iced_futures", @@ -2287,12 +2502,12 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "futures", "iced_core", "log", "rustc-hash 2.1.1", + "tokio", "wasm-bindgen-futures", "wasmtimer", ] @@ -2300,7 +2515,6 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "bitflags 2.11.0", "bytemuck", @@ -2321,7 +2535,6 @@ dependencies = [ [[package]] name = "iced_program" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "iced_graphics", "iced_runtime", @@ -2330,7 +2543,6 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -2342,9 +2554,9 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "bytes", + "cosmic-client-toolkit", "dnd", "iced_core", "iced_futures", @@ -2356,7 +2568,6 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "bytemuck", "cosmic-text", @@ -2373,9 +2584,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ - "as-raw-xcb-connection", "bitflags 2.11.0", "bytemuck", "cosmic-client-toolkit", @@ -2392,20 +2601,18 @@ dependencies = [ "rustc-hash 2.1.1", "rustix 0.38.44", "thiserror 2.0.18", - "tiny-xlib", "wayland-backend", "wayland-client", "wayland-protocols", "wayland-sys", "wgpu", - "x11rb", ] [[package]] name = "iced_widget" version = "0.14.2" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ + "cosmic-client-toolkit", "dnd", "iced_renderer", "iced_runtime", @@ -2418,6 +2625,37 @@ dependencies = [ "window_clipboard", ] +[[package]] +name = "iced_winit" +version = "0.14.0" +dependencies = [ + "cosmic-client-toolkit", + "cursor-icon", + "dnd", + "iced_debug", + "iced_futures", + "iced_graphics", + "iced_program", + "iced_runtime", + "log", + "raw-window-handle", + "rustc-hash 2.1.1", + "rustix 0.38.44", + "thiserror 2.0.18", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "web-sys", + "winapi", + "window_clipboard", + "winit 0.31.0-beta.2", + "winit-core 0.31.0-beta.2", + "xkbcommon 0.7.0", + "xkbcommon-dl", + "xkeysym", +] + [[package]] name = "icu_collections" version = "2.1.1" @@ -2542,9 +2780,9 @@ dependencies = [ [[package]] name = "image" -version = "0.25.9" +version = "0.25.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" dependencies = [ "bytemuck", "byteorder-lite", @@ -2798,6 +3036,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading", + "pkg-config", +] + [[package]] name = "khronos_api" version = "3.1.0" @@ -2806,9 +3055,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "known-folders" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770919970f7d2f74fea948900d35e2ef64f44129e8ae4015f59de1f0aca7c2a5" +checksum = "7a1886916523694cd6ea3d175f03a1e5010699a2a4cc13696d83d7bea1d80638" dependencies = [ "windows-sys 0.61.2", ] @@ -2877,23 +3126,25 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.182" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] -name = "libcosmic" -version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" +name = "libcosmic-yoda" +version = "0.1.0-yoda.2" dependencies = [ "apply", + "ashpd 0.12.3", "auto_enums", + "cosmic-client-toolkit", "cosmic-config", "cosmic-freedesktop-icons", "cosmic-settings-daemon", "cosmic-theme", "css-color", "derive_setters", + "enumflags2", "float-cmp 0.10.0", "futures", "i18n-embed", @@ -2904,18 +3155,21 @@ dependencies = [ "iced_renderer", "iced_runtime", "iced_tiny_skia", + "iced_wgpu", "iced_widget", + "iced_winit", "image", "jiff", "log", "palette", "phf 0.13.1", - "raw-window-handle", + "rfd", "rust-embed", "serde", "slotmap", "taffy", "thiserror 2.0.18", + "tokio", "tracing", "unicode-segmentation", "url", @@ -2963,7 +3217,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -2974,13 +3228,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.12" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" dependencies = [ "bitflags 2.11.0", "libc", - "redox_syscall 0.7.2", + "plain", + "redox_syscall 0.9.0", ] [[package]] @@ -3206,6 +3461,15 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "memmap2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +dependencies = [ + "libc", +] + [[package]] name = "memmap2" version = "0.9.10" @@ -3224,6 +3488,21 @@ dependencies = [ "autocfg", ] +[[package]] +name = "metal" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15" +dependencies = [ + "bitflags 2.11.0", + "block", + "core-graphics-types 0.2.0", + "foreign-types", + "log", + "objc", + "paste", +] + [[package]] name = "miette" version = "7.6.0" @@ -3231,13 +3510,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" dependencies = [ "cfg-if", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] name = "mime" version = "0.1.0" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "smithay-clipboard", ] @@ -3278,9 +3556,9 @@ checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" [[package]] name = "mio" -version = "1.1.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "log", @@ -3290,9 +3568,9 @@ dependencies = [ [[package]] name = "moxcms" -version = "0.7.11" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" dependencies = [ "num-traits", "pxfm", @@ -3306,9 +3584,9 @@ checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" [[package]] name = "naga" -version = "27.0.3" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8" +checksum = "618f667225063219ddfc61251087db8a9aec3c3f0950c916b614e403486f1135" dependencies = [ "arrayvec", "bit-set", @@ -3325,6 +3603,7 @@ dependencies = [ "num-traits", "once_cell", "rustc-hash 1.1.0", + "spirv", "thiserror 2.0.18", "unicode-ident", ] @@ -3545,6 +3824,22 @@ dependencies = [ "objc_id", ] +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + [[package]] name = "objc2" version = "0.6.4" @@ -3554,6 +3849,22 @@ dependencies = [ "objc2-encode", ] +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.11.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation 0.2.2", + "objc2-quartz-core", +] + [[package]] name = "objc2-app-kit" version = "0.3.2" @@ -3561,9 +3872,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ "bitflags 2.11.0", - "objc2", + "block2 0.6.2", + "objc2 0.6.4", "objc2-core-foundation", - "objc2-foundation", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.11.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -3573,9 +3897,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ "bitflags 2.11.0", - "block2", + "block2 0.6.2", "dispatch2", - "objc2", + "objc2 0.6.4", ] [[package]] @@ -3589,6 +3913,18 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + [[package]] name = "objc2-core-video" version = "0.3.2" @@ -3606,6 +3942,18 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.11.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", +] + [[package]] name = "objc2-foundation" version = "0.3.2" @@ -3613,11 +3961,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.11.0", - "block2", - "objc2", + "block2 0.6.2", + "objc2 0.6.4", "objc2-core-foundation", ] +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.11.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.11.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + [[package]] name = "objc2-ui-kit" version = "0.3.2" @@ -3625,9 +3998,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ "bitflags 2.11.0", - "objc2", + "objc2 0.6.4", "objc2-core-foundation", - "objc2-foundation", + "objc2-foundation 0.3.2", ] [[package]] @@ -3773,7 +4146,7 @@ dependencies = [ "libc", "redox_syscall 0.5.18", "smallvec", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -3950,6 +4323,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "png" version = "0.17.16" @@ -3990,6 +4369,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + [[package]] name = "portable-atomic" version = "1.13.1" @@ -4029,6 +4414,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + [[package]] name = "prettyplease" version = "0.2.37" @@ -4134,6 +4525,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" dependencies = [ "memchr", + "serde", ] [[package]] @@ -4157,6 +4549,8 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ + "libc", + "rand_chacha 0.3.1", "rand_core 0.6.4", ] @@ -4166,7 +4560,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "rand_chacha", + "rand_chacha 0.9.0", "rand_core 0.9.5", ] @@ -4181,6 +4575,16 @@ dependencies = [ "rand_core 0.10.0", ] +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_chacha" version = "0.9.0" @@ -4196,6 +4600,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] [[package]] name = "rand_core" @@ -4212,6 +4619,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +[[package]] +name = "range-alloc" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca45419789ae5a7899559e9512e58ca889e41f04f1f2445e9f4b290ceccd1d08" + [[package]] name = "rangemap" version = "1.7.1" @@ -4251,6 +4664,16 @@ dependencies = [ "font-types 0.11.0", ] +[[package]] +name = "redox_event" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5018d583d6d2f5499352aea8d177e9067d1eb03ab17c78169d5ba7a30001b15" +dependencies = [ + "bitflags 2.11.0", + "libredox", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -4262,9 +4685,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.7.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d94dd2f7cd932d4dc02cc8b2b50dfd38bd079a4e5d79198b99743d7fcf9a4b4" +checksum = "c5102a6aaa05aa011a238e178e6bca86d2cb56fc9f586d37cb80f5bca6e07759" dependencies = [ "bitflags 2.11.0", ] @@ -4378,6 +4801,30 @@ dependencies = [ "zune-jpeg 0.4.21", ] +[[package]] +name = "rfd" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" +dependencies = [ + "ashpd 0.11.1", + "block2 0.6.2", + "dispatch2", + "js-sys", + "log", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "pollster", + "raw-window-handle", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.60.2", +] + [[package]] name = "rgb" version = "0.8.52" @@ -4582,6 +5029,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sctk-adwaita" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd3accc0f3f4bbaf2c9e1957a030dc582028130c67660d44c0a0345a22ca69b" +dependencies = [ + "ab_glyph", + "log", + "memmap2 0.9.10", + "smithay-client-toolkit", + "tiny-skia", +] + [[package]] name = "self_cell" version = "1.2.2" @@ -4808,8 +5268,8 @@ dependencies = [ "cgmath", "cursor-icon", "downcast-rs", - "drm 0.14.1", - "drm-ffi 0.9.0", + "drm", + "drm-ffi", "drm-fourcc", "encoding_rs", "errno", @@ -4842,7 +5302,7 @@ dependencies = [ "wayland-protocols-misc", "wayland-protocols-wlr", "wayland-server", - "winit", + "winit 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "x11rb", "xkbcommon 0.9.0", ] @@ -4860,7 +5320,7 @@ dependencies = [ "cursor-icon", "libc", "log", - "memmap2", + "memmap2 0.9.10", "pkg-config", "rustix 1.1.4", "thiserror 2.0.18", @@ -4880,7 +5340,6 @@ dependencies = [ [[package]] name = "smithay-clipboard" version = "0.8.0" -source = "git+https://github.com/pop-os/smithay-clipboard?tag=sctk-0.20#859b02c88f45c554049a67c6ddeec1692ce0e20b" dependencies = [ "libc", "raw-window-handle", @@ -4921,22 +5380,30 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27207bb65232eda1f588cf46db2fee75c0808d557f6b3cf19a75f5d6d7c94df1" +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "softbuffer" version = "0.4.1" -source = "git+https://github.com/pop-os/softbuffer?tag=cosmic-4.0#a3f77e251e7422803f693df6e3fc313c010c4dcb" dependencies = [ "as-raw-xcb-connection", "bytemuck", "cfg_aliases", "cocoa", "core-graphics", - "drm 0.11.1", "fastrand", "foreign-types", "js-sys", "log", - "memmap2", + "memmap2 0.9.10", "objc", "raw-window-handle", "redox_syscall 0.5.18", @@ -4951,6 +5418,15 @@ dependencies = [ "x11rb", ] +[[package]] +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.11.0", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -5096,6 +5572,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -5241,6 +5726,45 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "tracing", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "0.5.11" @@ -5526,9 +6050,9 @@ checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] name = "unicode-vo" @@ -5542,6 +6066,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -5558,8 +6088,15 @@ dependencies = [ "idna", "percent-encoding", "serde", + "serde_derive", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "usvg" version = "0.37.0" @@ -5996,22 +6533,29 @@ checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] name = "wgpu" -version = "27.0.1" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77" +checksum = "f9cb534d5ffd109c7d1135f34cdae29e60eab94855a625dcfe1705f8bc7ad79f" dependencies = [ "arrayvec", "bitflags 2.11.0", + "bytemuck", "cfg-if", "cfg_aliases", "document-features", "hashbrown 0.16.1", + "js-sys", "log", + "naga", + "parking_lot", "portable-atomic", "profiling", "raw-window-handle", "smallvec", "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", "wgpu-core", "wgpu-hal", "wgpu-types", @@ -6019,9 +6563,9 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "27.0.3" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7" +checksum = "d23f4642f53f666adcfd2d3218ab174d1e6681101aef18696b90cbe64d1c10f9" dependencies = [ "arrayvec", "bit-set", @@ -6042,51 +6586,98 @@ dependencies = [ "rustc-hash 1.1.0", "smallvec", "thiserror 2.0.18", + "wgpu-core-deps-apple", + "wgpu-core-deps-emscripten", "wgpu-core-deps-windows-linux-android", "wgpu-hal", "wgpu-types", ] [[package]] -name = "wgpu-core-deps-windows-linux-android" -version = "27.0.0" +name = "wgpu-core-deps-apple" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3" +checksum = "87b7b696b918f337c486bf93142454080a32a37832ba8a31e4f48221890047da" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-emscripten" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b251c331f84feac147de3c4aa3aa45112622a95dd7ee1b74384fa0458dbd79" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-windows-linux-android" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ca976e72b2c9964eb243e281f6ce7f14a514e409920920dcda12ae40febaae" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-hal" -version = "27.0.4" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b21cb61c57ee198bc4aff71aeadff4cbb80b927beb912506af9c780d64313ce" +checksum = "44d6cb474beb218824dcc9e1ce679d973f719262789bfb27407da560cac20eeb" dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", "bitflags 2.11.0", + "block", + "bytemuck", "cfg-if", "cfg_aliases", + "core-graphics-types 0.2.0", + "glow", + "glutin_wgl_sys", + "gpu-allocator", + "gpu-descriptor", + "hashbrown 0.16.1", + "js-sys", + "khronos-egl", + "libc", "libloading", "log", + "metal", "naga", + "ndk-sys", + "objc", + "once_cell", + "ordered-float", + "parking_lot", "portable-atomic", "portable-atomic-util", + "profiling", + "range-alloc", "raw-window-handle", "renderdoc-sys", + "smallvec", "thiserror 2.0.18", + "wasm-bindgen", + "web-sys", "wgpu-types", + "windows 0.62.2", + "windows-core 0.62.2", ] [[package]] name = "wgpu-types" -version = "27.0.1" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb" +checksum = "e18308757e594ed2cd27dddbb16a139c42a683819d32a2e0b1b0167552f5840c" dependencies = [ "bitflags 2.11.0", "bytemuck", "js-sys", "log", - "thiserror 2.0.18", "web-sys", ] @@ -6124,18 +6715,72 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "window_clipboard" version = "0.4.1" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "clipboard-win", "clipboard_macos", "clipboard_wayland", - "clipboard_x11", "dnd", "mime 0.1.0", "raw-window-handle", "thiserror 1.0.69", ] +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections 0.2.0", + "windows-core 0.61.2", + "windows-future 0.2.1", + "windows-link 0.1.3", + "windows-numerics 0.2.0", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + [[package]] name = "windows-core" version = "0.62.2" @@ -6144,9 +6789,31 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading 0.1.0", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", ] [[package]] @@ -6171,19 +6838,63 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-result" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", ] [[package]] @@ -6192,7 +6903,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -6246,7 +6957,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -6301,7 +7012,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link", + "windows-link 0.2.1", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -6312,6 +7023,24 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -6492,6 +7221,30 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +[[package]] +name = "winit" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "cfg_aliases", + "cursor-icon", + "dpi 0.1.2", + "libc", + "raw-window-handle", + "rustix 1.1.4", + "smol_str", + "tracing", + "winit-android 0.31.0-beta.2", + "winit-appkit 0.31.0-beta.2", + "winit-common 0.31.0-beta.2", + "winit-core 0.31.0-beta.2", + "winit-orbital 0.31.0-beta.2", + "winit-uikit 0.31.0-beta.2", + "winit-wayland 0.31.0-beta.2", + "winit-web 0.31.0-beta.2", + "winit-win32 0.31.0-beta.2", +] + [[package]] name = "winit" version = "0.31.0-beta.2" @@ -6501,24 +7254,38 @@ dependencies = [ "bitflags 2.11.0", "cfg_aliases", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc", "raw-window-handle", "rustix 1.1.4", "smol_str", "tracing", - "winit-android", - "winit-appkit", - "winit-common", - "winit-core", - "winit-orbital", - "winit-uikit", - "winit-wayland", - "winit-web", - "winit-win32", + "winit-android 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-appkit 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-common 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-orbital 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-uikit 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-wayland 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-web 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-win32 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "winit-x11", ] +[[package]] +name = "winit-android" +version = "0.31.0-beta.2" +dependencies = [ + "android-activity", + "bitflags 2.11.0", + "dpi 0.1.2", + "ndk", + "raw-window-handle", + "smol_str", + "tracing", + "winit-core 0.31.0-beta.2", +] + [[package]] name = "winit-android" version = "0.31.0-beta.2" @@ -6527,12 +7294,33 @@ checksum = "51d9c0d2cd93efec3a9f9ad819cfaf0834782403af7c0d248c784ec0c61761df" dependencies = [ "android-activity", "bitflags 2.11.0", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "ndk", "raw-window-handle", "smol_str", "tracing", - "winit-core", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-appkit" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "block2 0.6.2", + "dispatch2", + "dpi 0.1.2", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-video", + "objc2-foundation 0.3.2", + "raw-window-handle", + "smol_str", + "tracing", + "winit-common 0.31.0-beta.2", + "winit-core 0.31.0-beta.2", ] [[package]] @@ -6542,20 +7330,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21310ca07851a49c348e0c2cc768e36b52ca65afda2c2354d78ed4b90074d8aa" dependencies = [ "bitflags 2.11.0", - "block2", + "block2 0.6.2", "dispatch2", - "dpi", - "objc2", - "objc2-app-kit", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", "objc2-core-foundation", "objc2-core-graphics", "objc2-core-video", - "objc2-foundation", + "objc2-foundation 0.3.2", "raw-window-handle", "smol_str", "tracing", - "winit-common", - "winit-core", + "winit-common 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-common" +version = "0.31.0-beta.2" +dependencies = [ + "memmap2 0.9.10", + "objc2 0.6.4", + "objc2-core-foundation", + "smol_str", + "tracing", + "winit-core 0.31.0-beta.2", + "xkbcommon-dl", ] [[package]] @@ -6564,16 +7365,29 @@ version = "0.31.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45375fbac4cbb77260d83a30b1f9d8105880dbac99a9ae97f56656694680ff69" dependencies = [ - "memmap2", - "objc2", + "memmap2 0.9.10", + "objc2 0.6.4", "objc2-core-foundation", "smol_str", "tracing", - "winit-core", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "x11-dl", "xkbcommon-dl", ] +[[package]] +name = "winit-core" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "cursor-icon", + "dpi 0.1.2", + "keyboard-types", + "raw-window-handle", + "smol_str", + "web-time", +] + [[package]] name = "winit-core" version = "0.31.0-beta.2" @@ -6582,13 +7396,28 @@ checksum = "e4f0ccd7abb43740e2c6124ac7cae7d865ecec74eec63783e8922577ac232583" dependencies = [ "bitflags 2.11.0", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "keyboard-types", "raw-window-handle", "smol_str", "web-time", ] +[[package]] +name = "winit-orbital" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "dpi 0.1.2", + "libredox", + "orbclient", + "raw-window-handle", + "redox_event", + "smol_str", + "tracing", + "winit-core 0.31.0-beta.2", +] + [[package]] name = "winit-orbital" version = "0.31.0-beta.2" @@ -6596,13 +7425,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51ea1fb262e7209f265f12bd0cc792c399b14355675e65531e9c8a87db287d46" dependencies = [ "bitflags 2.11.0", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "orbclient", "raw-window-handle", "redox_syscall 0.5.18", "smol_str", "tracing", - "winit-core", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-uikit" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "block2 0.6.2", + "dispatch2", + "dpi 0.1.2", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "objc2-ui-kit", + "raw-window-handle", + "smol_str", + "tracing", + "winit-common 0.31.0-beta.2", + "winit-core 0.31.0-beta.2", ] [[package]] @@ -6612,18 +7460,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "680a356e798837d8eb274d4556e83bceaf81698194e31aafc5cfb8a9f2fab643" dependencies = [ "bitflags 2.11.0", - "block2", + "block2 0.6.2", "dispatch2", - "dpi", - "objc2", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "objc2 0.6.4", "objc2-core-foundation", - "objc2-foundation", + "objc2-foundation 0.3.2", "objc2-ui-kit", "raw-window-handle", "smol_str", "tracing", - "winit-common", - "winit-core", + "winit-common 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-wayland" +version = "0.31.0-beta.2" +dependencies = [ + "ahash", + "bitflags 2.11.0", + "calloop", + "cursor-icon", + "dpi 0.1.2", + "libc", + "memmap2 0.9.10", + "raw-window-handle", + "rustix 1.1.4", + "sctk-adwaita", + "smithay-client-toolkit", + "smol_str", + "tracing", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", + "winit-common 0.31.0-beta.2", + "winit-core 0.31.0-beta.2", ] [[package]] @@ -6636,9 +7509,9 @@ dependencies = [ "bitflags 2.11.0", "calloop", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc", - "memmap2", + "memmap2 0.9.10", "raw-window-handle", "rustix 1.1.4", "smithay-client-toolkit", @@ -6648,8 +7521,29 @@ dependencies = [ "wayland-client", "wayland-protocols", "wayland-protocols-plasma", - "winit-common", - "winit-core", + "winit-common 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-web" +version = "0.31.0-beta.2" +dependencies = [ + "atomic-waker", + "bitflags 2.11.0", + "concurrent-queue", + "cursor-icon", + "dpi 0.1.2", + "js-sys", + "pin-project", + "raw-window-handle", + "smol_str", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "web-time", + "winit-core 0.31.0-beta.2", ] [[package]] @@ -6662,7 +7556,7 @@ dependencies = [ "bitflags 2.11.0", "concurrent-queue", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "js-sys", "pin-project", "raw-window-handle", @@ -6672,7 +7566,22 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "web-time", - "winit-core", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-win32" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "cursor-icon", + "dpi 0.1.2", + "raw-window-handle", + "smol_str", + "tracing", + "unicode-segmentation", + "windows-sys 0.59.0", + "winit-core 0.31.0-beta.2", ] [[package]] @@ -6683,13 +7592,13 @@ checksum = "644ea78af0e858aa3b092e5d1c67c41995a98220c81813f1353b28bc8bb91eaa" dependencies = [ "bitflags 2.11.0", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "raw-window-handle", "smol_str", "tracing", "unicode-segmentation", "windows-sys 0.59.0", - "winit-core", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -6702,15 +7611,15 @@ dependencies = [ "bytemuck", "calloop", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc", "percent-encoding", "raw-window-handle", "rustix 1.1.4", "smol_str", "tracing", - "winit-common", - "winit-core", + "winit-common 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "x11-dl", "x11rb", "xkbcommon-dl", @@ -6734,6 +7643,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -6883,6 +7801,17 @@ dependencies = [ "libc", ] +[[package]] +name = "xkbcommon" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" +dependencies = [ + "libc", + "memmap2 0.8.0", + "xkeysym", +] + [[package]] name = "xkbcommon" version = "0.8.0" @@ -6890,7 +7819,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d66ca9352cbd4eecbbc40871d8a11b4ac8107cfc528a6e14d7c19c69d0e1ac9" dependencies = [ "libc", - "memmap2", + "memmap2 0.9.10", "xkeysym", ] @@ -6901,7 +7830,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a974f48060a14e95705c01f24ad9c3345022f4d97441b8a36beb7ed5c4a02d" dependencies = [ "libc", - "memmap2", + "memmap2 0.9.10", "xkeysym", ] @@ -6976,9 +7905,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.14.0" +version = "5.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca82f95dbd3943a40a53cfded6c2d0a2ca26192011846a1810c4256ef92c60bc" +checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285" dependencies = [ "async-broadcast", "async-executor", @@ -6999,21 +7928,46 @@ dependencies = [ "rustix 1.1.4", "serde", "serde_repr", + "tokio", "tracing", "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow 0.7.14", + "winnow 1.0.3", "zbus_macros", "zbus_names", "zvariant", ] [[package]] -name = "zbus_macros" -version = "5.14.0" +name = "zbus-lockstep" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" +checksum = "6998de05217a084b7578728a9443d04ea4cd80f2a0839b8d78770b76ccd45863" +dependencies = [ + "zbus_xml", + "zvariant", +] + +[[package]] +name = "zbus-lockstep-macros" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10da05367f3a7b7553c8cdf8fa91aee6b64afebe32b51c95177957efc47ca3a0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "zbus-lockstep", + "zbus_xml", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -7026,12 +7980,24 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.3.1" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" +checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" dependencies = [ "serde", - "winnow 0.7.14", + "winnow 1.0.3", + "zvariant", +] + +[[package]] +name = "zbus_xml" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8067892e940ed1727dea64690378601603b31d62dfde019a5335fbb7c0e0ed9" +dependencies = [ + "quick-xml", + "serde", + "zbus_names", "zvariant", ] @@ -7154,23 +8120,24 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.10.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" +checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0" dependencies = [ "endi", "enumflags2", "serde", - "winnow 0.7.14", + "url", + "winnow 1.0.3", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.10.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" +checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -7181,13 +8148,17 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" +checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6" dependencies = [ "proc-macro2", "quote", "serde", "syn", - "winnow 0.7.14", + "winnow 1.0.3", ] + +[[patch.unused]] +name = "winit-x11" +version = "0.31.0-beta.2" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 9c8eecdc..ae16192b 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.90" +channel = "1.93" components = [ "rust-src" ]