From e4bb42f2c065b8df2ddbe67e23c9f420012034df Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 23 Jul 2026 18:49:35 +0200 Subject: [PATCH 01/80] i18n: translation updates from weblate Co-authored-by: Prasanta-Hembram --- resources/i18n/sat/cosmic_comp.ftl | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/i18n/sat/cosmic_comp.ftl diff --git a/resources/i18n/sat/cosmic_comp.ftl b/resources/i18n/sat/cosmic_comp.ftl new file mode 100644 index 00000000..e69de29b From 53a8d0bea2c1a361c1a0c57b20b4adc4b748c2a0 Mon Sep 17 00:00:00 2001 From: Timo Strunk Date: Wed, 22 Jul 2026 22:31:41 +0200 Subject: [PATCH 02/80] fix(kms): join surface threads before exit to avoid SIGABRT on shutdown When the kiosk child exits, calling process::exit() directly from the calloop callback runs libc's atexit handlers (Mesa's util_queue_kill_threads) while a KMS surface thread may still be mid-frame in eglCreateSync(). Concurrent Mesa teardown and live EGL use corrupt the heap and glibc aborts the process ("free(): corrupted unsorted chunks", SIGABRT). Stop the event loop cleanly via should_stop instead, stashing the child's exit code in Common::kiosk_exit_code. After the loop returns, pause() each DRM device (releasing master so the surface drop path skips its blocking clear_state(), and the incoming compositor gets master immediately), then drop_and_join() every surface so no thread is left in Mesa. Only then is process::exit() called, at which point atexit is safe to run. The join is done here rather than in Surface::Drop because an unconditional join in Drop deadlocks against apply_config_for_outputs during normal operation; once the event loop has stopped, the surface thread can process ThreadCommand::End and exit without the main thread servicing any pending message. https://github.com/pop-os/cosmic-comp/issues/2375 Authored by Claude Opus 4.8 Reviewed by Timo Strunk Co-Authored-By: Claude Opus 4.8 --- src/lib.rs | 36 ++++++++++++++++++++++++++++-------- src/state.rs | 2 ++ 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 542162eb..f7282942 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ use smithay::{ }; use anyhow::{Context, Result}; -use state::{LastRefresh, State}; +use state::{BackendData, LastRefresh, State}; use std::{ env, ffi::OsString, @@ -222,19 +222,17 @@ pub fn run(hooks: crate::hooks::Hooks) -> Result<(), Box> { // Kiosk child exited with status Ok(Some(exit_status)) => { info!("Command exited with status {:?}", exit_status); - match exit_status.code() { - // Exiting with the same status as the kiosk child - Some(code) => process::exit(code), - // The kiosk child exited with signal, exiting with error - None => process::exit(1), - } + // Stop cleanly so surface threads are joined before exit() (signal -> 1). + state.common.kiosk_exit_code = Some(exit_status.code().unwrap_or(1)); + state.common.should_stop = true; } // Command still running Ok(None) => {} // Kiosk child disappeared, exiting with error Err(err) => { warn!(?err, "Failed to wait for command"); - process::exit(1); + state.common.kiosk_exit_code = Some(1); + state.common.should_stop = true; } } } @@ -245,10 +243,32 @@ pub fn run(hooks: crate::hooks::Hooks) -> Result<(), Box> { let _ = child.kill(); } + let kiosk_exit_code = state.common.kiosk_exit_code; + + // Join surface threads before exit() so no thread is mid-eglCreateSync when + // Mesa's atexit handlers run and corrupt the heap (issue #2375). Safe here + // because the event loop has stopped; an unconditional join in Surface::Drop + // would instead deadlock against apply_config_for_outputs. + if let BackendData::Kms(kms) = &mut state.backend { + // Release master first so the surface drop path skips its blocking commit. + for device in kms.drm_devices.values_mut() { + device.drm.pause(); + } + for device in kms.drm_devices.values_mut() { + for (_, surface) in device.inner.surfaces.drain() { + surface.drop_and_join(); + } + } + } + // drop eventloop & state before logger std::mem::drop(event_loop); std::mem::drop(state); + if let Some(code) = kiosk_exit_code { + process::exit(code); + } + Ok(()) } diff --git a/src/state.rs b/src/state.rs index 21cbb400..ce546505 100644 --- a/src/state.rs +++ b/src/state.rs @@ -244,6 +244,7 @@ pub struct Common { pub clock: Clock, pub startup_done: Arc, pub should_stop: bool, + pub kiosk_exit_code: Option, pub gesture_state: Option, @@ -754,6 +755,7 @@ impl State { clock, startup_done: Arc::new(AtomicBool::new(false)), should_stop: false, + kiosk_exit_code: None, gesture_state: None, kiosk_child: None, From 24d29008f0ce00074612b49987e47468db3ef231 Mon Sep 17 00:00:00 2001 From: Timo Strunk Date: Wed, 22 Jul 2026 22:31:41 +0200 Subject: [PATCH 03/80] fix(kms): skip empty DRM cleanup commit in display_configuration display_configuration() issued an ALLOW_MODESET atomic commit on every udev event, even when no planes needed detaching. The empty commit is a no-op modeset that interferes with in-flight commits on the render thread, and it fails with EPERM whenever we don't hold DRM master (e.g. a render-only secondary GPU), aborting device enumeration. Track whether any property was added and skip the commit when there is nothing to do. A commit that does have changes still propagates its error as before, so a genuine failure to apply cleanup is not silently swallowed. https://github.com/pop-os/cosmic-comp/issues/2375 Authored by Claude Opus 4.8 Reviewed by Timo Strunk Co-Authored-By: Claude Opus 4.8 --- src/backend/kms/drm_helpers.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/backend/kms/drm_helpers.rs b/src/backend/kms/drm_helpers.rs index 8629c9d7..46659613 100644 --- a/src/backend/kms/drm_helpers.rs +++ b/src/backend/kms/drm_helpers.rs @@ -75,6 +75,7 @@ pub fn display_configuration( // And then cleanup if device.is_atomic() { let mut req = AtomicModeReq::new(); + let mut has_changes = false; let plane_handles = device.plane_handles()?; // We cannot just shortcut and use the legacy api for all cleanups because of this. @@ -95,10 +96,15 @@ pub fn display_configuration( 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)); + has_changes = true; } } } - device.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, req)?; + // Skip an empty commit: a no-op modeset that also fails with EPERM + // without DRM master (e.g. a render-only secondary GPU). + if has_changes { + device.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, req)?; + } } else { for crtc in res_handles.crtcs() { #[allow(deprecated)] From 6108cef8b368653c51c0bbe6f81132a5a80b5c46 Mon Sep 17 00:00:00 2001 From: RipleyTom Date: Fri, 24 Jul 2026 09:56:52 +0200 Subject: [PATCH 04/80] fix: workaround for Chromium apps breaking below 1.0 scaling --- src/state.rs | 7 +++++-- src/wayland/handlers/fractional_scale.rs | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/state.rs b/src/state.rs index ce546505..24ccd9e1 100644 --- a/src/state.rs +++ b/src/state.rs @@ -956,8 +956,11 @@ impl Common { ); if let Some(output) = primary_scanout_output { with_fractional_scale(states, |fraction_scale| { - fraction_scale - .set_preferred_scale(output.current_scale().fractional_scale()); + // The 1.0 clamp is a workaround for Chromium + // TODO: remove if Chromium ever gets fixed + fraction_scale.set_preferred_scale( + output.current_scale().fractional_scale().max(1.0), + ); }); } } diff --git a/src/wayland/handlers/fractional_scale.rs b/src/wayland/handlers/fractional_scale.rs index aea0cc7e..dcc8d7f9 100644 --- a/src/wayland/handlers/fractional_scale.rs +++ b/src/wayland/handlers/fractional_scale.rs @@ -49,7 +49,10 @@ impl FractionalScaleHandler for State { with_states(&surface, |states| { with_fractional_scale(states, |fractional_scale| { - fractional_scale.set_preferred_scale(output.current_scale().fractional_scale()); + // The 1.0 clamp is a workaround for Chromium + // TODO: remove if Chromium ever gets fixed + fractional_scale + .set_preferred_scale(output.current_scale().fractional_scale().max(1.0)); }); }); } From 44de3063ee30ba704592fd6c06db62639292f9f2 Mon Sep 17 00:00:00 2001 From: Tom Grushka Date: Thu, 23 Jul 2026 22:43:56 -0600 Subject: [PATCH 05/80] dbus: Fix deadlock in a11y keyboard monitor A11yKeyboardMonitorState::key_event calls self.has_key_grab() inside self.clients guard loop. A "well-behaved" a11y client calls `WatchKeyboard` before `SetKeyGrabs`. If WatchKeyboard is not called first, the `!client.watched` guard does not short-circuit, so `has_key_grab()` runs and locks `self.clients` again. This causes a deadlock and the compositor freezes, requiring a hard reboot. This commit moves the `self.has_key_grab()` before the guard to prevent a double lock. --- src/dbus/a11y_keyboard_monitor.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dbus/a11y_keyboard_monitor.rs b/src/dbus/a11y_keyboard_monitor.rs index cf66a54b..c97aa590 100644 --- a/src/dbus/a11y_keyboard_monitor.rs +++ b/src/dbus/a11y_keyboard_monitor.rs @@ -150,9 +150,10 @@ impl A11yKeyboardMonitorState { } pub fn key_event(&self, modifiers: &ModifiersState, keysym: &KeysymHandle, state: KeyState) { + let has_key_grab = self.has_key_grab(modifiers, keysym.modified_sym()); 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()) { + if !client.watched && !has_key_grab { continue; } From 5ce21fb4f3de9671d6cb6c4738d9bd02d541a57a Mon Sep 17 00:00:00 2001 From: RipleyTom Date: Thu, 23 Jul 2026 19:28:32 +0200 Subject: [PATCH 06/80] fix: ensure X window focus on unmap/map --- src/shell/focus/mod.rs | 23 +++++++++++++++++++++++ src/shell/focus/target.rs | 2 +- src/xwayland.rs | 2 +- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index 4e88c499..cab37a85 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -212,6 +212,29 @@ impl Shell { state.common.shell.write().update_active(); } + // We suppress Element(X) to Fullscreen(X) transition to avoid + // loss of focus by the X window when having a transition to fullscreen + // but in the case of X11 unmap/map the leave/enter needs to happen for the X11 + // internal state to be focused on the window + pub fn set_focus_on_x11_map( + state: &mut State, + target: &KeyboardFocusTarget, + seat: &Seat, + update_cursor: bool, + ) { + let need_reset = seat + .get_keyboard() + .and_then(|keyboard| keyboard.current_focus()) + .and_then(|current| current.x11_surface()) + .is_some_and(|current| Some(current) == target.x11_surface()); + + if need_reset { + update_focus_state(seat, None, state, None, false); + } + + Shell::set_focus(state, Some(target), seat, None, update_cursor); + } + pub fn append_focus_stack(&mut self, target: impl Into, seat: &Seat) { let target = target.into(); if target.is_minimized() { diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index 929cd92e..80b1a34f 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -326,7 +326,7 @@ impl KeyboardFocusTarget { } } - fn x11_surface(&self) -> Option { + pub fn x11_surface(&self) -> Option { match self { KeyboardFocusTarget::Element(mapped) => mapped.active_window().x11_surface().cloned(), KeyboardFocusTarget::Fullscreen(surface) => surface.x11_surface().cloned(), diff --git a/src/xwayland.rs b/src/xwayland.rs index 25b8b0e0..83978b6e 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -858,7 +858,7 @@ impl XwmHandler for State { if let Some(target) = res { let seat = shell.seats.last_active().clone(); std::mem::drop(shell); - Shell::set_focus(self, Some(&target), &seat, None, false); + Shell::set_focus_on_x11_map(self, &target, &seat, false); } } } From 9514b4945edc2656b38a34419c3a8618f490cb17 Mon Sep 17 00:00:00 2001 From: v0up3r Date: Mon, 27 Jul 2026 19:21:35 -0300 Subject: [PATCH 07/80] fix(kms): don't panic when renderer creation fails after a GPU reset When the DRM device is lost transiently, for example when the amdgpu driver performs a GPU reset and recovers ("device wedged, but recovered through reset"), acquiring a renderer in `SurfaceThreadState::redraw` fails. The three `renderer`/`single_renderer` calls used `.unwrap()`, so the failure panicked the surface's render thread. Because that panic unwinds across the EGL/GBM FFI boundary it aborts the whole process (SIGABRT), dropping the session back to the greeter and closing every running application. `redraw` is already fallible and its caller logs the error and reschedules a redraw, and the frame-submission path (`render_frame`/`queue_frame`) already propagates errors the same way. Propagate the renderer-acquisition errors too, so a transient device loss is retried on the next frame, once the device is back, instead of taking down the compositor. Relates to #649. Assisted by an AI coding tool; I have reviewed and fully understand the change and am able to maintain it and respond to review. --- src/backend/kms/surface/mod.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/backend/kms/surface/mod.rs b/src/backend/kms/surface/mod.rs index bb28bc3c..eb232377 100644 --- a/src/backend/kms/surface/mod.rs +++ b/src/backend/kms/surface/mod.rs @@ -1006,12 +1006,16 @@ impl SurfaceThreadState { &self.shell.read(), ); + // Acquiring a renderer can fail transiently when the underlying DRM + // device is lost (e.g. after a GPU reset). let mut renderer = if render_node != self.target_node { self.api .renderer(&render_node, &self.target_node, compositor.format()) - .unwrap() + .map_err(|err| anyhow::format_err!("Failed to create renderer: {:?}", err))? } else { - self.api.single_renderer(&self.target_node).unwrap() + self.api + .single_renderer(&self.target_node) + .map_err(|err| anyhow::format_err!("Failed to create renderer: {:?}", err))? }; self.timings.start_render(&self.clock); @@ -1267,7 +1271,10 @@ impl SurfaceThreadState { }) .context("Failed to draw to offscreen render target")?; - renderer = self.api.single_renderer(&self.target_node).unwrap(); + renderer = self + .api + .single_renderer(&self.target_node) + .map_err(|err| anyhow::format_err!("Failed to create renderer: {:?}", err))?; elements = postprocess_elements( &mut renderer, From 8d450d497763912c4575c0f28dda661dafd4366c Mon Sep 17 00:00:00 2001 From: Hojjat Date: Mon, 27 Jul 2026 15:41:58 -0600 Subject: [PATCH 08/80] fix: clamp pointer edge after applying the output offset --- src/input/mod.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 0cb26a9d..39bff9dc 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -393,10 +393,12 @@ impl State { // - output_geometry.size so that we don't send leave events to a fullscreen app // - logical size so that the position doesn't end up outside the actual size of the output // See https://github.com/pop-os/cosmic-comp/pull/2568 - let max_x = output_geometry_loc.x - + logical.w.min(output_geometry.size.w as f64).next_down(); - let max_y = output_geometry_loc.y - + logical.h.min(output_geometry.size.h as f64).next_down(); + let max_x = (output_geometry_loc.x + + logical.w.min(output_geometry.size.w as f64)) + .next_down(); + let max_y = (output_geometry_loc.y + + logical.h.min(output_geometry.size.h as f64)) + .next_down(); position.x = position.x.clamp(output_geometry_loc.x, max_x); position.y = position.y.clamp(output_geometry_loc.y, max_y); From ffeda3375a7e60ace6ae64b19432f1f0c1fc1034 Mon Sep 17 00:00:00 2001 From: zyads Date: Mon, 27 Jul 2026 12:13:52 -0400 Subject: [PATCH 09/80] fix(corner_radius): check LayerHookId for layer surfaces GetCornerRadiusLayer looked up the existing hook via ToplevelHookId while inserting into LayerHookId a few lines later. Both are Mutex)>> so it compiles, but they are distinct data_map slots: the "does this surface already have a corner-radius object?" check for a layer surface read the toplevel slot, so it could not see a layer object that was already there, and it created a toplevel entry on a layer surface as a side effect. Every other read of the layer hook already uses LayerHookId. Disclosure: found and written with AI assistance (Claude). The change is the one line quoted above; I understand it and verified the tree builds with it. --- src/wayland/protocols/corner_radius.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wayland/protocols/corner_radius.rs b/src/wayland/protocols/corner_radius.rs index be6f9ca0..51dc1153 100644 --- a/src/wayland/protocols/corner_radius.rs +++ b/src/wayland/protocols/corner_radius.rs @@ -190,7 +190,7 @@ where let radius_exists = with_states(surface.wl_surface(), |surface_data| { let hook_id = surface_data .data_map - .get_or_insert_threadsafe(|| ToplevelHookId::new(None)); + .get_or_insert_threadsafe(|| LayerHookId::new(None)); let guard = hook_id.lock().unwrap(); guard.as_ref().map(|(_, t)| t.upgrade().is_ok()) }); From d687285338dc35947a3262c119e92313ee02ef01 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 29 Jul 2026 11:01:49 +0200 Subject: [PATCH 10/80] i18n: translation updates from weblate Co-authored-by: Andi Chandler Co-authored-by: Davide Co-authored-by: Sproston Co-authored-by: Wilbur Lindqvist Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-comp/en_GB/ Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-comp/it/ Translation: Pop OS/COSMIC Comp --- resources/i18n/en-GB/cosmic_comp.ftl | 20 ++++++++++++++++++++ resources/i18n/it/cosmic_comp.ftl | 6 +++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/resources/i18n/en-GB/cosmic_comp.ftl b/resources/i18n/en-GB/cosmic_comp.ftl index e69de29b..0bd9c20b 100644 --- a/resources/i18n/en-GB/cosmic_comp.ftl +++ b/resources/i18n/en-GB/cosmic_comp.ftl @@ -0,0 +1,20 @@ +a11y-zoom-move-continuously = View moves continuously with pointer +a11y-zoom-move-onedge = View moves when pointer reaches edge +a11y-zoom-move-centered = View moves to keep pointer centred +a11y-zoom-settings = Magnifier settings... +grow-window = Grow +shrink-window = Shrink +swap-windows = Swap Windows +stack-windows = Stack Windows +unknown-keybinding = +window-menu-minimize = Minimise +window-menu-maximize = Maximise +window-menu-fullscreen = Fullscreen +window-menu-tiled = Float window +window-menu-screenshot = Take screenshot +window-menu-move = Move +window-menu-resize = Resize +window-menu-move-prev-workspace = Move to previous workspace +window-menu-move-next-workspace = Move to next workspace +window-menu-stack = Create window stack +window-menu-unstack-all = Unstack windows diff --git a/resources/i18n/it/cosmic_comp.ftl b/resources/i18n/it/cosmic_comp.ftl index ba92609f..b1eb56c3 100644 --- a/resources/i18n/it/cosmic_comp.ftl +++ b/resources/i18n/it/cosmic_comp.ftl @@ -1,4 +1,4 @@ -grow-window = Ingrandisci +grow-window = Crescere shrink-window = Riduci swap-windows = Scambia le finestre stack-windows = Sovrapponi le finestre @@ -6,7 +6,7 @@ unknown-keybinding = window-menu-minimize = Minimizza window-menu-maximize = Massimizza window-menu-tiled = Finestra flottante -window-menu-screenshot = Cattura una schermata +window-menu-screenshot = Scatta uno screenshot window-menu-move = Sposta window-menu-resize = Ridimensiona window-menu-move-prev-workspace = Sposta all'area di lavoro precedente @@ -22,7 +22,7 @@ window-menu-resize-edge-left = Sinistra window-menu-resize-edge-right = Destra window-menu-resize-edge-bottom = In basso window-menu-fullscreen = Schermo intero -a11y-zoom-settings = Opzioni lente d'ingrandimento... +a11y-zoom-settings = Impostazioni lente d'ingrandimento... a11y-zoom-move-continuously = Segue sempre il cursorse a11y-zoom-move-centered = Mantiene il cursore centrato a11y-zoom-move-onedge = Segue il cursore ai bordi From 091583ac84abac02967ae358cf9570ddfef63b31 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 9 Jan 2026 13:47:31 +0100 Subject: [PATCH 11/80] backend: Support wl-dmabuf v6 --- Cargo.lock | 30 ++- Cargo.toml | 2 +- src/backend/kms/device.rs | 32 +-- src/backend/kms/mod.rs | 186 ++++++++++++++---- src/backend/kms/surface/mod.rs | 64 +++--- src/backend/winit.rs | 5 +- src/backend/x11.rs | 5 +- src/shell/element/surface.rs | 5 +- src/state.rs | 11 +- src/utils/global.rs | 28 +++ src/utils/mod.rs | 1 + src/wayland/handlers/dmabuf.rs | 19 +- src/wayland/protocols/drm.rs | 91 ++++++--- .../protocols/output_configuration/mod.rs | 29 +-- 14 files changed, 298 insertions(+), 210 deletions(-) create mode 100644 src/utils/global.rs diff --git a/Cargo.lock b/Cargo.lock index fd733056..aec3b990 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -312,12 +312,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "atomic_float" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628d228f918ac3b82fe590352cc719d30664a0c13ca3a60266fe02c7132d480a" - [[package]] name = "atomicwrites" version = "0.4.2" @@ -1194,7 +1188,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1508,7 +1502,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2695,7 +2689,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2818,7 +2812,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1886916523694cd6ea3d175f03a1e5010699a2a4cc13696d83d7bea1d80638" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3428,7 +3422,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -4524,7 +4518,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4819,12 +4813,11 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/smithay/smithay.git?rev=1ed69cb#1ed69cb55e9d0e9f38887c3b7d53d24365c4b14f" +source = "git+https://github.com/smithay/smithay.git?rev=cdc03f7#cdc03f73ce57d3352c0c57a69971e3e16544051c" dependencies = [ "aliasable", "appendlist", "ash", - "atomic_float", "bitflags 2.11.0", "calloop", "cc", @@ -4846,6 +4839,7 @@ dependencies = [ "libseat", "pixman", "pkg-config", + "portable-atomic", "profiling", "rand 0.9.2", "reis", @@ -5113,7 +5107,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5887,9 +5881,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.12" +version = "0.32.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" +checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" dependencies = [ "bitflags 2.11.0", "wayland-backend", @@ -6132,7 +6126,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3f8a9e73..fb9fa0bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,4 +144,4 @@ lto = "fat" cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } [patch.crates-io] -smithay = { git = "https://github.com/smithay/smithay.git", rev = "1ed69cb" } +smithay = { git = "https://github.com/smithay/smithay.git", rev = "cdc03f7" } diff --git a/src/backend/kms/device.rs b/src/backend/kms/device.rs index 44682dc5..418a16f3 100644 --- a/src/backend/kms/device.rs +++ b/src/backend/kms/device.rs @@ -59,7 +59,7 @@ use std::{ time::Duration, }; -use super::{drm_helpers, socket::Socket, surface::Surface}; +use super::{drm_helpers, surface::Surface}; #[derive(Debug)] pub struct EGLInternals { @@ -98,14 +98,12 @@ pub struct Device { pub texture_formats: FormatSet, event_token: Option, - pub socket: Option, } #[derive(Debug)] struct ReusableDevice { leasing_global: Option, active_clients: HashSet, - socket: Option, } #[derive(Debug)] @@ -593,7 +591,7 @@ impl State { } } - pub fn device_removed(&mut self, dev: dev_t, dh: &DisplayHandle) -> Result<()> { + pub fn device_removed(&mut self, dev: dev_t, _dh: &DisplayHandle) -> Result<()> { let backend = self.backend.kms(); // we can't use DrmNode::from_node_id, because that assumes the node is still on sysfs let drm_node = backend @@ -616,13 +614,6 @@ impl State { if let Some(token) = device.event_token.take() { self.common.event_loop_handle.remove(token); } - if let Some(socket) = device.socket.take() { - self.common.event_loop_handle.remove(socket.token); - self.common - .dmabuf_state - .destroy_global::(dh, socket.dmabuf_global); - dh.remove_global::(socket.drm_global); - } backend.api.as_mut().remove_node(&device.inner.render_node); backend .primary_node @@ -755,23 +746,7 @@ impl Device { let ReusableDevice { leasing_global, active_clients, - socket, } = reuse.unwrap_or_else(|| { - let socket = match (!is_software) - .then(|| common.create_socket(dh, render_node, texture_formats.clone())) - .transpose() - { - Ok(socket) => socket, - Err(err) => { - warn!( - ?err, - "Failed to initialize hardware-acceleration for clients on {}.", - render_node, - ); - None - } - }; - let leasing_global = match (!is_software) .then(|| DrmLeaseState::new::(dh, &dev_node)) .transpose() @@ -790,7 +765,6 @@ impl Device { ReusableDevice { leasing_global, active_clients: HashSet::new(), - socket, } }); @@ -831,7 +805,6 @@ impl Device { texture_formats, event_token: Some(token), - socket, }) } @@ -892,7 +865,6 @@ impl Device { let device = ReusableDevice { leasing_global: self.inner.leasing_global, active_clients: self.inner.active_clients, - socket: self.socket, }; let state = OldDeviceState { diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 16559e4e..df711387 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -4,12 +4,15 @@ use crate::{ config::{CompOutputConfig, ScreenFilter}, shell::Shell, state::BackendData, - utils::{env::dev_var, prelude::*}, - wayland::protocols::output_power::OutputPowerState, + utils::{env::dev_var, global::remove_global_with_timer, prelude::*}, + wayland::protocols::{drm::WlDrmState, output_power::OutputPowerState}, }; -use anyhow::{Context, Result}; -use calloop::LoopSignal; +use anyhow::{self, Context, Result}; +use calloop::{ + LoopSignal, + timer::{TimeoutAction, Timer}, +}; use cosmic_comp_config::output::comp::{AdaptiveSync, OutputState}; use indexmap::IndexMap; use render::gles::GbmGlowBackend; @@ -32,11 +35,12 @@ use smithay::{ control::{Device as _, connector::Interface, crtc}, }, input::{self, Libinput}, + wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1::TrancheFlags, wayland_server::{Client, DisplayHandle}, }, utils::{Clock, DevPath, Monotonic, Size}, wayland::{ - dmabuf::DmabufGlobal, + dmabuf::{DmabufFeedbackBuilder, DmabufGlobal}, drm_syncobj::{DrmSyncobjState, supports_syncobj_eventfd}, relative_pointer::RelativePointerManagerState, }, @@ -48,12 +52,12 @@ use std::{ collections::{HashMap, HashSet}, path::Path, sync::{Arc, RwLock, atomic::AtomicBool}, + time::Duration, }; mod device; mod drm_helpers; pub mod render; -mod socket; mod surface; use device::*; pub(crate) use surface::Surface; @@ -74,6 +78,7 @@ pub struct KmsState { libinput: Libinput, pub syncobj_state: Option, + pub dmabuf_global: Option, } pub struct KmsGuard<'a> { @@ -131,6 +136,7 @@ pub fn init_backend( libinput: libinput_context, syncobj_state: None, + dmabuf_global: None, }); // manually add already present gpus @@ -142,9 +148,10 @@ pub fn init_backend( } } - if let Err(err) = state.backend.kms().select_primary_gpu(dh) { + if let Err(err) = state.select_primary_gpu(dh) { warn!("Failed to determine primary gpu: {}", err); } + state.update_default_feedback(); if let Err(err) = state.refresh_output_config() { info!( @@ -317,11 +324,13 @@ fn init_udev( { let backend = state.backend.kms(); - if matches!(event, UdevEvent::Added { .. } | UdevEvent::Removed { .. }) - && backend.primary_node.read().unwrap().is_none() - && let Err(err) = state.backend.kms().select_primary_gpu(&dh) - { - warn!("Failed to determine a new primary gpu: {}", err); + if matches!(event, UdevEvent::Added { .. } | UdevEvent::Removed { .. }) { + if backend.primary_node.read().unwrap().is_none() + && let Err(err) = state.select_primary_gpu(&dh) + { + warn!("Failed to determine a new primary gpu: {}", err); + } + state.update_default_feedback(); } } @@ -465,50 +474,118 @@ impl State { device.drm.pause(); } } -} -impl KmsState { fn select_primary_gpu(&mut self, dh: &DisplayHandle) -> Result<()> { // We don't have to check the allow/blocklist here, // as any disallowed devices won't be in `self.drm_devices`. - let mut primary_node = self.primary_node.write().unwrap(); + let kms = self.backend.kms(); + let mut primary_node = kms.primary_node.write().unwrap(); let _ = primary_node.take(); // if we error don't leave an old node in place - *primary_node = determine_primary_gpu(&self.drm_devices, self.session.seat())?; + *primary_node = determine_primary_gpu(&kms.drm_devices, kms.session.seat())?; if let Some(node) = *primary_node { info!("Using {} as primary gpu for rendering.", node); - self.software_renderer.take(); - } else if self.software_renderer.is_none() { + kms.software_renderer.take(); + + // setup minimal feedback. We will update it in `update_default_feedback` + let primary_formats = kms + .drm_devices + .values() + .find(|dev| dev.inner.render_node == node) + .unwrap() + .texture_formats + .clone(); + let feedback = DmabufFeedbackBuilder::new(node.dev_id(), primary_formats.clone()) + .build() + .unwrap(); + + if let Some(global) = kms.dmabuf_global.as_ref() { + self.common + .dmabuf_state + .set_default_feedback(global, &feedback); + } else { + let dmabuf_global = self + .common + .dmabuf_state + .create_global_with_default_feedback::(dh, &feedback); + kms.dmabuf_global = Some(dmabuf_global); + }; + + let device_path = node + .dev_path_with_type(NodeType::Render) + .or_else(|| node.dev_path()) + .ok_or(anyhow::anyhow!( + "Could not determine path for gpu node: {}", + node + ))?; + + if let Some(drm) = self.common.wl_drm_state.as_mut() { + drm.update_device(device_path, primary_formats); + } else { + self.common.wl_drm_state = Some(WlDrmState::new::( + dh, + device_path, + primary_formats, + kms.dmabuf_global.as_ref().unwrap(), + )); + } + } else if kms.software_renderer.is_none() { info!("Failed to find a suitable gpu, using software renderingr"); - self.software_renderer = match software_renderer() { + kms.software_renderer = match software_renderer() { Ok(renderer) => Some(renderer), Err(err) => { error!(?err, "Failed to initialize software EGL renderer."); None } }; + + if let Some(drm) = self.common.wl_drm_state.take() { + remove_global_with_timer(dh, &self.common.event_loop_handle, drm.global().clone()); + } + if let Some(global) = kms.dmabuf_global.take() { + self.common + .dmabuf_state + .disable_global::(dh, &global); + let source = Timer::from_duration(Duration::from_secs(5)); + let res = + self.common + .event_loop_handle + .insert_source(source, move |_, _, state| { + state + .common + .dmabuf_state + .destroy_global::(&state.common.display_handle, global); + TimeoutAction::Drop + }); + if let Err(err) = res { + tracing::error!( + "failed to insert timer source to destroy output global: {}", + err + ); + } + } } if !crate::utils::env::bool_var("COSMIC_DISABLE_SYNCOBJ").unwrap_or(false) { if let Some(primary_node) = primary_node .as_ref() .and_then(|node| node.node_with_type(NodeType::Primary).and_then(|x| x.ok())) - && let Some(device) = self.drm_devices.get(&primary_node) + && let Some(device) = kms.drm_devices.get(&primary_node) { let import_device = device.drm.device().device_fd().clone(); if supports_syncobj_eventfd(&import_device) { - if let Some(state) = self.syncobj_state.as_mut() { + if let Some(state) = kms.syncobj_state.as_mut() { state.update_device(import_device); } else { let syncobj_state = DrmSyncobjState::new::(dh, import_device); - self.syncobj_state = Some(syncobj_state); + kms.syncobj_state = Some(syncobj_state); } return Ok(()); } } - if let Some(old_state) = self.syncobj_state.take() { + if let Some(old_state) = kms.syncobj_state.take() { dh.remove_global::(old_state.into_global()); } } @@ -516,6 +593,45 @@ impl KmsState { Ok(()) } + fn update_default_feedback(&mut self) { + let kms = self.backend.kms(); + let primary_node = kms.primary_node.read().unwrap(); + if let Some(primary_node) = *primary_node { + let primary_formats = kms + .drm_devices + .values() + .find(|dev| dev.inner.render_node == primary_node) + .unwrap() + .texture_formats + .clone(); + + let mut feedback = + DmabufFeedbackBuilder::new(primary_node.dev_id(), primary_formats.clone()); + for dev in kms + .drm_devices + .values() + .filter(|dev| dev.inner.render_node != primary_node) + { + feedback = feedback.add_preference_tranche( + dev.inner.render_node.dev_id(), + TrancheFlags::Sampling, + dev.texture_formats.iter().cloned(), + 6..=6, + ); + } + + let default_feedback = feedback.build().unwrap(); + self.common.dmabuf_state.set_default_feedback( + kms.dmabuf_global + .as_ref() + .expect("Primary node but no dmabuf global?"), + &default_feedback, + ); + } + } +} + +impl KmsState { pub fn switch_vt(&mut self, num: i32) -> Result<(), anyhow::Error> { self.session.change_vt(num).map_err(Into::into) } @@ -523,29 +639,25 @@ impl KmsState { pub fn dmabuf_imported( &mut self, client: Option, - global: &DmabufGlobal, + _global: &DmabufGlobal, dmabuf: Dmabuf, ) -> Result { + let device_node = dmabuf + .node() + .unwrap_or_else(|| self.primary_node.read().unwrap().unwrap()); let mut device = self .drm_devices .values_mut() - .find(|device| { - device - .socket - .as_ref() - .map(|s| &s.dmabuf_global == global) - .unwrap_or(false) - }) - .context("Couldn't find gpu for dmabuf global")?; + .find(|dev| dev.inner.render_node == device_node) + .ok_or(anyhow::anyhow!( + "Unable to find device for node: {}", + device_node + ))?; // If device advertised to client doesn't support format/modifier, select // first device that does. This is needed for image-copy from // output/toplevel on a different node. - // - // TODO: After - // https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/268, - // only try the device specified explicitly by the client, if set. - if !device.texture_formats.contains(&dmabuf.format()) { + if dmabuf.node().is_none() && !device.texture_formats.contains(&dmabuf.format()) { device = self .drm_devices .values_mut() diff --git a/src/backend/kms/surface/mod.rs b/src/backend/kms/surface/mod.rs index eb232377..cbc89381 100644 --- a/src/backend/kms/surface/mod.rs +++ b/src/backend/kms/surface/mod.rs @@ -1529,7 +1529,7 @@ fn get_surface_dmabuf_feedback( render_node: DrmNode, target_node: DrmNode, render_formats: FormatSet, - _target_formats: FormatSet, + target_formats: FormatSet, primary_plane_formats: FormatSet, overlay_plane_formats: Option, ) -> SurfaceDmabufFeedback { @@ -1547,60 +1547,40 @@ fn get_surface_dmabuf_feedback( .cloned() .collect::() }); - let builder = DmabufFeedbackBuilder::new(render_node.dev_id(), render_formats); - /* - // Sadly no implementation would pick this up as a preferred render tranche, - // where the combined formats would increase our chances of doing a dmabuf copy. - // .. So we should probably not advertise this on the off-chance it actually triggers bugs. - // + let mut builder = DmabufFeedbackBuilder::new(render_node.dev_id(), render_formats.clone()); - let combined_formats = render_formats.intersection(&target_formats).cloned().collect::(); - if target_node != render_node.dev_id() && !combined_formats.is_empty() { - builder = builder.add_preference_tranche( - render_node.dev_id(), - None, - combined_formats, - ); - }; - - // We also can't advertise scan out tranches for the actual display device, - // as e.g. the nvidia driver might then send us dmabufs, that makes e.g. the iris hangs on import... - if target_node != render_node.dev_id() && !combined_formats.is_empty() { + if target_node != render_node { builder = builder.add_preference_tranche( target_node.dev_id(), - Some(zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout), - combined_formats, + zwp_linux_dmabuf_feedback_v1::TrancheFlags::Sampling, + target_formats, + 6..=6, ); }; - - // So no fun combinations, we gotta wait for dmabuf-v6 - */ - let render_feedback = builder.clone().build().unwrap(); - let primary_scanout_feedback = (target_node == render_node).then(|| { + + let primary_scanout_feedback = builder + .clone() + .add_preference_tranche( + target_node.dev_id(), + zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout, + primary_plane_formats, + 4..=6, + ) + .build() + .unwrap(); + let overlay_scanout_feedback = overlay_plane_formats.map(|formats| { builder - .clone() .add_preference_tranche( - render_node.dev_id(), - Some(zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout), - primary_plane_formats, + target_node.dev_id(), + zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout, + formats, + 4..=6, ) .build() .unwrap() }); - let overlay_scanout_feedback = overlay_plane_formats - .filter(|_| target_node == render_node) - .map(|formats| { - builder - .add_preference_tranche( - render_node.dev_id(), - Some(zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout), - formats, - ) - .build() - .unwrap() - }); SurfaceDmabufFeedback { render_feedback, diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 0ff249a7..24ef64b4 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -6,6 +6,7 @@ use crate::{ shell::{Devices, SeatExt}, state::{BackendData, Common}, utils::prelude::*, + wayland::protocols::drm::WlDrmState, }; use anyhow::{Context, Result, anyhow}; use cosmic_comp_config::output::comp::{OutputConfig, TransformDef}; @@ -273,7 +274,7 @@ fn init_egl_client_side( .create_global_with_default_feedback::(dh, &feedback); let render_node = render_node.unwrap().unwrap(); - let _drm_global_id = state.common.wl_drm_state.create_global::( + state.common.wl_drm_state = Some(WlDrmState::new::( dh, render_node .dev_path_with_type(NodeType::Render) @@ -284,7 +285,7 @@ fn init_egl_client_side( ))?, dmabuf_formats, &dmabuf_global, - ); + )); info!("EGL hardware-acceleration enabled."); } diff --git a/src/backend/x11.rs b/src/backend/x11.rs index bd0e1b34..98b27453 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -6,6 +6,7 @@ use crate::{ shell::{Devices, SeatExt}, state::{BackendData, Common}, utils::prelude::*, + wayland::protocols::drm::WlDrmState, }; use anyhow::{Context, Result, anyhow}; use cosmic_comp_config::output::comp::OutputConfig; @@ -497,7 +498,7 @@ where .common .dmabuf_state .create_global_with_default_feedback::(dh, &default_feedback); - let _drm_global_id = state.common.wl_drm_state.create_global::( + state.common.wl_drm_state = Some(WlDrmState::new::( dh, render_node .dev_path_with_type(NodeType::Render) @@ -508,7 +509,7 @@ where ))?, renderer.dmabuf_formats(), &dmabuf_global, - ); + )); info!("EGL hardware-acceleration enabled."); diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index e146efeb..99bb2f78 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -838,10 +838,7 @@ impl CosmicSurface { self.0 .send_dmabuf_feedback(output, primary_scan_out_output, |_, data| { if is_fullscreen { - feedback - .primary_scanout_feedback - .as_ref() - .unwrap_or(&feedback.render_feedback) + &feedback.primary_scanout_feedback } else if frame_time_filter_fn(data) == Kind::ScanoutCandidate { feedback .overlay_scanout_feedback diff --git a/src/state.rs b/src/state.rs index 24ccd9e1..8cfee1a9 100644 --- a/src/state.rs +++ b/src/state.rs @@ -276,7 +276,7 @@ pub struct Common { pub idle_inhibiting_surfaces: HashSet, pub shm_state: ShmState, pub cursor_shape_manager_state: CursorShapeManagerState, - pub wl_drm_state: WlDrmState>, + pub wl_drm_state: Option>>, pub viewporter_state: ViewporterState, pub kde_decoration_state: KdeDecorationState, pub xdg_decoration_state: XdgDecorationState, @@ -326,7 +326,7 @@ pub enum LockedBackend<'a> { pub struct SurfaceDmabufFeedback { pub render_feedback: DmabufFeedback, pub overlay_scanout_feedback: Option, - pub primary_scanout_feedback: Option, + pub primary_scanout_feedback: DmabufFeedback, } #[derive(Debug)] @@ -671,7 +671,7 @@ impl State { let cursor_shape_manager_state = CursorShapeManagerState::new::(dh); let seat_state = SeatState::::new(); let viewporter_state = ViewporterState::new::(dh); - let wl_drm_state = WlDrmState::>::default(); + let wl_drm_state = None; let kde_decoration_state = KdeDecorationState::new::(dh, Mode::Client); let xdg_decoration_state = XdgDecorationState::new::(dh); let session_lock_manager_state = @@ -1066,10 +1066,7 @@ impl Common { surface, render_element_states, &feedback.render_feedback, - feedback - .primary_scanout_feedback - .as_ref() - .unwrap_or(&feedback.render_feedback), + &feedback.primary_scanout_feedback, ) }, ) diff --git a/src/utils/global.rs b/src/utils/global.rs new file mode 100644 index 00000000..e309f68a --- /dev/null +++ b/src/utils/global.rs @@ -0,0 +1,28 @@ +use std::time::Duration; + +use calloop::{ + LoopHandle, + timer::{TimeoutAction, Timer}, +}; +use smithay::reexports::wayland_server::DisplayHandle; +use wayland_backend::server::GlobalId; + +pub fn remove_global_with_timer( + dh: &DisplayHandle, + event_loop_handle: &LoopHandle, + id: GlobalId, +) { + dh.disable_global::(id.clone()); + let source = Timer::from_duration(Duration::from_secs(5)); + let dh = dh.clone(); + let res = event_loop_handle.insert_source(source, move |_, _, _state| { + dh.remove_global::(id.clone()); + TimeoutAction::Drop + }); + if let Err(err) = res { + tracing::error!( + "failed to insert timer source to destroy output global: {}", + err + ); + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index ac1df019..401597cc 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -4,6 +4,7 @@ pub mod env; mod ids; pub(crate) use self::ids::id_gen; pub mod geometry; +pub mod global; pub mod iced; pub mod prelude; pub mod quirks; diff --git a/src/wayland/handlers/dmabuf.rs b/src/wayland/handlers/dmabuf.rs index 4d9732bb..572fe01b 100644 --- a/src/wayland/handlers/dmabuf.rs +++ b/src/wayland/handlers/dmabuf.rs @@ -40,7 +40,7 @@ impl DmabufHandler for State { fn new_surface_feedback( &mut self, surface: &WlSurface, - global: &DmabufGlobal, + _global: &DmabufGlobal, ) -> Option { let BackendData::Kms(kms) = &self.backend else { return None; @@ -57,18 +57,7 @@ impl DmabufHandler for State { f.ended_at.is_none() && f.surface.has_surface(surface, WindowSurfaceType::all()) }); - let node = kms - .drm_devices - .values() - .find(|device| { - device - .socket - .as_ref() - .map(|s| &s.dmabuf_global == global) - .unwrap_or(false) - })? - .inner - .render_node; + let node = kms.primary_node.read().unwrap().unwrap(); let kms_surface = kms .drm_devices .values() @@ -77,9 +66,7 @@ impl DmabufHandler for State { Some(with_states(surface, |data| { if is_fullscreen { - feedback - .primary_scanout_feedback - .unwrap_or(feedback.render_feedback) + feedback.primary_scanout_feedback } else if frame_time_filter_fn(data) == Kind::ScanoutCandidate { feedback .overlay_scanout_feedback diff --git a/src/wayland/protocols/drm.rs b/src/wayland/protocols/drm.rs index c86e5ff0..d2902c98 100644 --- a/src/wayland/protocols/drm.rs +++ b/src/wayland/protocols/drm.rs @@ -38,7 +38,11 @@ use smithay::{ }; use tracing::trace; -use std::{convert::TryFrom, path::PathBuf, sync::Arc}; +use std::{ + convert::TryFrom, + path::PathBuf, + sync::{Arc, Mutex}, +}; pub enum ImportError { Failed, @@ -52,19 +56,28 @@ pub trait DrmHandler { } } -#[derive(Debug, Default)] -pub struct WlDrmState(std::marker::PhantomData); +#[derive(Debug)] +pub struct WlDrmState { + device: Arc>, + global: GlobalId, + _marker: std::marker::PhantomData, +} + +#[derive(Debug)] +pub struct DrmGlobalDeviceData { + formats: Vec, + path: PathBuf, +} /// Data associated with a drm global. pub struct DrmGlobalData { filter: Box Fn(&'a Client) -> bool + Send + Sync>, - formats: Arc>, - device_path: PathBuf, dmabuf_global: DmabufGlobal, + device: Arc>, } pub struct DrmInstanceData { - formats: Arc>, + device: Arc>, dmabuf_global: DmabufGlobal, } @@ -86,16 +99,24 @@ where data_init: &mut DataInit<'_, D>, ) { let data = DrmInstanceData { - formats: global_data.formats.clone(), + device: global_data.device.clone(), dmabuf_global: global_data.dmabuf_global, }; let drm_instance = data_init.init(resource, data); - drm_instance.device(global_data.device_path.to_string_lossy().into_owned()); + drm_instance.device( + global_data + .device + .lock() + .unwrap() + .path + .to_string_lossy() + .into_owned(), + ); if drm_instance.version() >= 2 { drm_instance.capabilities(wl_drm::Capability::Prime as u32); } - for format in global_data.formats.iter() { + for format in global_data.device.lock().unwrap().formats.iter() { if let Ok(converted) = wl_drm::Format::try_from(*format as u32) { drm_instance.format(converted as u32); } @@ -148,7 +169,7 @@ where } => { let format = match Fourcc::try_from(format) { Ok(format) => { - if !data.formats.contains(&format) { + if !data.device.lock().unwrap().formats.contains(&format) { drm.post_error( wl_drm::Error::InvalidFormat, String::from("Format not advertised by wl_drm"), @@ -220,13 +241,12 @@ where } impl WlDrmState { - pub fn create_global( - &mut self, + pub fn new( display: &DisplayHandle, device_path: PathBuf, formats: FormatSet, dmabuf_global: &DmabufGlobal, - ) -> GlobalId + ) -> Self where D: GlobalDispatch + Dispatch @@ -234,19 +254,16 @@ impl WlDrmState { + DmabufHandler + 'static, { - self.create_global_with_filter::(display, device_path, formats, dmabuf_global, |_| { - true - }) + Self::new_with_filter::(display, device_path, formats, dmabuf_global, |_| true) } - pub fn create_global_with_filter( - &mut self, + pub fn new_with_filter( display: &DisplayHandle, device_path: PathBuf, formats: FormatSet, dmabuf_global: &DmabufGlobal, client_filter: F, - ) -> GlobalId + ) -> Self where D: GlobalDispatch + Dispatch @@ -255,21 +272,45 @@ impl WlDrmState { + 'static, F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static, { - let formats = Arc::new( - formats + let device = Arc::new(Mutex::new(DrmGlobalDeviceData { + formats: formats .into_iter() .filter(|f| f.modifier == Modifier::Invalid) .map(|f| f.code) .collect(), - ); + path: device_path, + })); + let data = DrmGlobalData { filter: Box::new(client_filter), - formats, - device_path, + device: device.clone(), dmabuf_global: *dmabuf_global, }; - display.create_global::(2, data) + let global = display.create_global::(2, data); + + WlDrmState { + device, + global, + _marker: Default::default(), + } + } + + pub fn update_device(&mut self, device_path: PathBuf, formats: FormatSet) { + let new_device = DrmGlobalDeviceData { + formats: formats + .into_iter() + .filter(|f| f.modifier == Modifier::Invalid) + .map(|f| f.code) + .collect(), + path: device_path, + }; + + *self.device.lock().unwrap() = new_device; + } + + pub fn global(&self) -> &GlobalId { + &self.global } } diff --git a/src/wayland/protocols/output_configuration/mod.rs b/src/wayland/protocols/output_configuration/mod.rs index 4bd1c0bd..d413e634 100644 --- a/src/wayland/protocols/output_configuration/mod.rs +++ b/src/wayland/protocols/output_configuration/mod.rs @@ -1,9 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only -use calloop::{ - LoopHandle, - timer::{TimeoutAction, Timer}, -}; +use calloop::LoopHandle; use cosmic_comp_config::output::comp::AdaptiveSync; use cosmic_protocols::output_management::v1::server::{ zcosmic_output_configuration_head_v1::ZcosmicOutputConfigurationHeadV1, @@ -30,7 +27,7 @@ use smithay::{ utils::{Logical, Physical, Point, Size, Transform}, wayland::output::WlOutputData, }; -use std::{convert::TryFrom, sync::Mutex, time::Duration}; +use std::{convert::TryFrom, sync::Mutex}; mod handlers; @@ -519,26 +516,6 @@ where } } -fn remove_global_with_timer( - dh: &DisplayHandle, - event_loop_handle: &LoopHandle, - id: GlobalId, -) { - dh.disable_global::(id.clone()); - let source = Timer::from_duration(Duration::from_secs(5)); - let dh = dh.clone(); - let res = event_loop_handle.insert_source(source, move |_, _, _state| { - dh.remove_global::(id.clone()); - TimeoutAction::Drop - }); - if let Err(err) = res { - tracing::error!( - "failed to insert timer source to destroy output global: {}", - err - ); - } -} - macro_rules! delegate_output_configuration { ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ @@ -578,4 +555,4 @@ macro_rules! delegate_output_configuration { } pub(crate) use delegate_output_configuration; -use crate::utils::prelude::OutputExt; +use crate::utils::{global::remove_global_with_timer, prelude::OutputExt}; From 789e0b363457094392697f65c40ac0047d29cc95 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 30 Jul 2026 13:03:22 +0200 Subject: [PATCH 12/80] i18n: translation updates from weblate Co-authored-by: Andi Chandler Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-comp/en_GB/ Translation: Pop OS/COSMIC Comp --- resources/i18n/en-GB/cosmic_comp.ftl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/resources/i18n/en-GB/cosmic_comp.ftl b/resources/i18n/en-GB/cosmic_comp.ftl index 0bd9c20b..853cc97d 100644 --- a/resources/i18n/en-GB/cosmic_comp.ftl +++ b/resources/i18n/en-GB/cosmic_comp.ftl @@ -18,3 +18,11 @@ window-menu-move-prev-workspace = Move to previous workspace window-menu-move-next-workspace = Move to next workspace window-menu-stack = Create window stack window-menu-unstack-all = Unstack windows +window-menu-unstack = Unstack window +window-menu-sticky = Sticky window +window-menu-close = Close +window-menu-close-all = Close all windows +window-menu-resize-edge-top = Top +window-menu-resize-edge-left = Left +window-menu-resize-edge-right = Right +window-menu-resize-edge-bottom = Bottom From 58c018dd40eb35047751dc3b68d9d31d14c03923 Mon Sep 17 00:00:00 2001 From: Hojjat Date: Fri, 31 Jul 2026 13:42:00 -0600 Subject: [PATCH 13/80] fix(shell): take presentation feedback from fullscreen surfaces --- src/shell/mod.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 39d692e2..d9f95976 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -5024,6 +5024,20 @@ impl Shell { let mut output_presentation_feedback = OutputPresentationFeedback::new(output); if let Some(active) = self.active_space(output) { + for fs in active.get_fullscreen_surfaces() { + fs.surface.take_presentation_feedback( + &mut output_presentation_feedback, + surface_primary_scanout_output, + |surface, _| { + surface_presentation_feedback_flags_from_states( + surface, + None, + render_element_states, + ) + }, + ); + } + active.mapped().for_each(|mapped| { mapped.active_window().take_presentation_feedback( &mut output_presentation_feedback, From f89c78b9bd58e4d8d6c9097c5778659e49417d58 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 5 Aug 2026 16:02:29 +0200 Subject: [PATCH 14/80] i18n: translation updates from weblate Co-authored-by: Hosted Weblate Co-authored-by: Martin Meizoso Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-comp/gl/ Translation: Pop OS/COSMIC Comp --- resources/i18n/gl/cosmic_comp.ftl | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 resources/i18n/gl/cosmic_comp.ftl diff --git a/resources/i18n/gl/cosmic_comp.ftl b/resources/i18n/gl/cosmic_comp.ftl new file mode 100644 index 00000000..1176cad6 --- /dev/null +++ b/resources/i18n/gl/cosmic_comp.ftl @@ -0,0 +1,28 @@ +a11y-zoom-move-continuously = A vista móvese continuamente co punteiro +a11y-zoom-move-onedge = A vista móvese cando o punteiro chega ao bordo +a11y-zoom-move-centered = Mover a vista para manter o cursor centrado +a11y-zoom-settings = Configuración da lupa... +grow-window = Aumentar +shrink-window = Encoller +swap-windows = Cambiar xanelas +stack-windows = Apilar xanelas +unknown-keybinding = +window-menu-minimize = Minimizar +window-menu-maximize = Maximizar +window-menu-fullscreen = Pantalla Completa +window-menu-tiled = Xanela flotante +window-menu-screenshot = Capturar pantalla +window-menu-move = Mover +window-menu-resize = Redimensionar +window-menu-move-prev-workspace = Mover ao espazo de traballo anterior +window-menu-move-next-workspace = Mover ao seguinte espazo de traballo +window-menu-stack = Crear pila de xanelas +window-menu-unstack-all = Desapilar xanelas +window-menu-unstack = Desapilar xanela +window-menu-sticky = Xanela persistente +window-menu-close = Pechar +window-menu-close-all = Pechar todas as xanelas +window-menu-resize-edge-top = Arriba +window-menu-resize-edge-left = Esquerda +window-menu-resize-edge-right = Dereita +window-menu-resize-edge-bottom = Fondo From abfb25a672353ece2ef7d1960be93f1ff74ea195 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Fri, 14 Aug 2026 08:51:31 +0200 Subject: [PATCH 15/80] i18n: translation updates from weblate Co-authored-by: Michael Millet Co-authored-by: Steen Rabol Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-comp/da/ Translation: Pop OS/COSMIC Comp --- resources/i18n/da/cosmic_comp.ftl | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/resources/i18n/da/cosmic_comp.ftl b/resources/i18n/da/cosmic_comp.ftl index e69de29b..1cee8eac 100644 --- a/resources/i18n/da/cosmic_comp.ftl +++ b/resources/i18n/da/cosmic_comp.ftl @@ -0,0 +1,24 @@ +a11y-zoom-move-continuously = Visningen bevæger sig kontinuerligt med markøren +a11y-zoom-move-onedge = Vis bevægelser nÃ¥r markør rammer kant +a11y-zoom-move-centered = Vis bevægelser, for at holde markør centreret +a11y-zoom-settings = Forstørrelsesglas-indstillinger... +grow-window = Gro +shrink-window = Skrump +swap-windows = Byt Vinduer +stack-windows = Stabel Vinduer +window-menu-tiled = Gør vindue flydende +window-menu-screenshot = Tag skærmbillede +window-menu-move = Flyt +window-menu-resize = Skift størrelse +window-menu-move-prev-workspace = Flyt til forrige arbejdsomrÃ¥de +window-menu-move-next-workspace = Flyt til næste arbejdsomrÃ¥de +window-menu-stack = Opret vinduestak +window-menu-unstack-all = Udtag vinduer af stak +window-menu-unstack = Udtag vindue af stak +window-menu-sticky = Fastgjort vindue +window-menu-close = Luk +window-menu-close-all = Luk alle vinduer +window-menu-resize-edge-top = Top +window-menu-resize-edge-left = Venstre +window-menu-resize-edge-right = Højre +window-menu-resize-edge-bottom = Bund From 6af1dba69bf4a2de31d4334fd3f8c934fdc62248 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Mon, 17 Aug 2026 08:51:26 +0200 Subject: [PATCH 16/80] i18n: translation updates from weblate Co-authored-by: Michael Millet Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-comp/da/ Translation: Pop OS/COSMIC Comp --- resources/i18n/da/cosmic_comp.ftl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/resources/i18n/da/cosmic_comp.ftl b/resources/i18n/da/cosmic_comp.ftl index 1cee8eac..bd0e1edb 100644 --- a/resources/i18n/da/cosmic_comp.ftl +++ b/resources/i18n/da/cosmic_comp.ftl @@ -22,3 +22,6 @@ window-menu-resize-edge-top = Top window-menu-resize-edge-left = Venstre window-menu-resize-edge-right = Højre window-menu-resize-edge-bottom = Bund +window-menu-minimize = Minimér +window-menu-maximize = Maksimér +window-menu-fullscreen = Fuld skærm From 18ae0826150757436e0c4d37140797c3879fe873 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 4 Aug 2026 01:09:16 -0700 Subject: [PATCH 17/80] Update `smithay` with tablet API changes For now, only `WlSurface` receives tablet focus, as was previously the case. Also updates `reis` to latest bugfix release. --- Cargo.lock | 41 ++++---- Cargo.toml | 2 +- src/input/mod.rs | 135 +++++++++++++++++-------- src/wayland/handlers/tablet_manager.rs | 4 +- 4 files changed, 119 insertions(+), 63 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aec3b990..f2364e9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1275,7 +1275,7 @@ checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f" dependencies = [ "bitflags 2.11.0", "bytemuck", - "drm-ffi 0.9.0", + "drm-ffi 0.9.1", "drm-fourcc", "libc", "rustix 0.38.44", @@ -1293,12 +1293,12 @@ dependencies = [ [[package]] name = "drm-ffi" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8e41459d99a9b529845f6d2c909eb9adf3b6d2f82635ae40be8de0601726e8b" +checksum = "51a91c9b32ac4e8105dec255e849e0d66e27d7c34d184364fb93e469db08f690" dependencies = [ - "drm-sys 0.8.0", - "rustix 0.38.44", + "drm-sys 0.8.1", + "rustix 1.1.4", ] [[package]] @@ -1319,12 +1319,12 @@ dependencies = [ [[package]] name = "drm-sys" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bafb66c8dbc944d69e15cfcc661df7e703beffbaec8bd63151368b06c5f9858c" +checksum = "ecc8e1361066d91f5ffccff060a3c3be9c3ecde15be2959c1937595f7a82a9f8" dependencies = [ "libc", - "linux-raw-sys 0.6.5", + "linux-raw-sys 0.9.4", ] [[package]] @@ -1502,7 +1502,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2689,7 +2689,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3060,6 +3060,12 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -4348,9 +4354,9 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "reis" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3fedd2777cde52c1be5e572efbec485eac7b801c47820eda388d4f13b9c4b" +checksum = "4b967ec6489a42067a20724f11987bb96178ea796873cbe1afd25fb93ee6f85f" dependencies = [ "calloop", "enumflags2", @@ -4518,7 +4524,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4813,7 +4819,7 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/smithay/smithay.git?rev=cdc03f7#cdc03f73ce57d3352c0c57a69971e3e16544051c" +source = "git+https://github.com/smithay/smithay.git?rev=5fb12b8#5fb12b87407b3680135c45d94214c5f1b1d0fbea" dependencies = [ "aliasable", "appendlist", @@ -4824,8 +4830,9 @@ dependencies = [ "cursor-icon", "downcast-rs", "drm 0.14.1", - "drm-ffi 0.9.0", + "drm-ffi 0.9.1", "drm-fourcc", + "drm-sys 0.8.1", "encoding_rs", "errno", "gbm", @@ -5107,7 +5114,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5406,7 +5413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f7c95348f20c1c913d72157b3c6dee6ea3e30b3d19502c5a7f6d3f160dacbf" dependencies = [ "cc", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index fb9fa0bc..cfe97687 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,4 +144,4 @@ lto = "fat" cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } [patch.crates-io] -smithay = { git = "https://github.com/smithay/smithay.git", rev = "cdc03f7" } +smithay = { git = "https://github.com/smithay/smithay.git", rev = "5fb12b8" } diff --git a/src/input/mod.rs b/src/input/mod.rs index 39bff9dc..f323cf4a 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -56,6 +56,7 @@ use smithay::{ GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent, PointerGrab, PointerHandle, RelativeMotionEvent, }, + tablet::{TabletDescriptor, TabletSeatTrait, tool}, touch::{DownEvent, MotionEvent as TouchMotionEvent, UpEvent}, }, output::Output, @@ -70,7 +71,6 @@ use smithay::{ keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat, pointer_constraints::{PointerConstraint, with_pointer_constraint}, seat::WaylandFocus, - tablet_manager::{TabletDescriptor, TabletSeatTrait}, }, }; use tracing::{error, trace}; @@ -179,7 +179,7 @@ impl State { let led_state = seat.get_keyboard().unwrap().led_state(); seat.devices().add_device(&device, led_state); if device.has_capability(DeviceCapability::TabletTool) { - seat.tablet_seat().add_tablet::( + seat.tablet_seat().add_wp_tablet( &self.common.display_handle, &TabletDescriptor::from(&device), ); @@ -1441,37 +1441,36 @@ impl State { let tablet_seat = seat.tablet_seat(); - let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&event.device())); let tool = tablet_seat.get_tool(&event.tool()); - if let (Some(tablet), Some(tool)) = (tablet, tool) { - if event.pressure_has_changed() { - tool.pressure(event.pressure()); - } - if event.distance_has_changed() { - tool.distance(event.distance()); - } - if event.tilt_has_changed() { - tool.tilt(event.tilt()); - } - if event.slider_has_changed() { - tool.slider_position(event.slider_position()); - } - if event.rotation_has_changed() { - tool.rotation(event.rotation()); - } - if event.wheel_has_changed() { - tool.wheel(event.wheel_delta(), event.wheel_delta_discrete()); - } + if let Some(tool) = tool { + let frame = tool::AxisFrame { + pressure: event.pressure_has_changed().then(|| event.pressure()), + distance: event.distance_has_changed().then(|| event.distance()), + + tilt: event.tilt_has_changed().then(|| event.tilt()), + rotation: event.rotation_has_changed().then(|| event.rotation()), + + slider: event.slider_has_changed().then(|| event.slider_position()), + wheel: event + .wheel_has_changed() + .then(|| (event.wheel_delta(), event.wheel_delta_discrete())), + }; + + tool.axis(self, frame); tool.motion( - position.as_logical(), + self, under .and_then(|(f, loc)| f.wl_surface().map(|s| (s.into_owned(), loc))), - &tablet, - SERIAL_COUNTER.next_serial(), - event.time_msec(), + &tool::MotionEvent { + location: position.as_logical(), + serial: SERIAL_COUNTER.next_serial(), + time: event.time_msec(), + }, ); + + tool.frame(self, event.time_msec()); } } } @@ -1509,25 +1508,53 @@ impl State { let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&event.device())); let dh = self.common.display_handle.clone(); - let tool = tablet_seat.add_tool::(self, &dh, &event.tool()); + let tool = tablet_seat + .get_tool(&event.tool()) + .unwrap_or_else(|| tablet_seat.add_wp_tool(self, &dh, &event.tool())); if let Some(tablet) = tablet { + let serial = SERIAL_COUNTER.next_serial(); + + let frame = tool::AxisFrame { + pressure: event.pressure_has_changed().then(|| event.pressure()), + distance: event.distance_has_changed().then(|| event.distance()), + + tilt: event.tilt_has_changed().then(|| event.tilt()), + rotation: event.rotation_has_changed().then(|| event.rotation()), + + slider: event.slider_has_changed().then(|| event.slider_position()), + wheel: event + .wheel_has_changed() + .then(|| (event.wheel_delta(), event.wheel_delta_discrete())), + }; + match event.state() { ProximityState::In => { - if let Some(under) = under.and_then(|(f, loc)| { + let under = under.and_then(|(f, loc)| { f.wl_surface().map(|s| (s.into_owned(), loc)) - }) { - tool.proximity_in( - position.as_logical(), - under, - &tablet, - SERIAL_COUNTER.next_serial(), - event.time_msec(), - ) - } + }); + tool.proximity_in( + self, + under, + tablet, + &tool::ProximityInEvent { + location: position.as_logical(), + axis: Some(frame), + serial: SERIAL_COUNTER.next_serial(), + time: event.time_msec(), + }, + ) } - ProximityState::Out => tool.proximity_out(event.time_msec()), + ProximityState::Out => tool.proximity_out( + self, + &tool::ProximityOutEvent { + serial, + time: event.time_msec(), + }, + ), } + + tool.frame(self, event.time_msec()); } } } @@ -1543,14 +1570,29 @@ impl State { self.common.idle_notifier_state.notify_activity(&seat); notify_cursor_activity(self, &seat); if let Some(tool) = seat.tablet_seat().get_tool(&event.tool()) { + let serial = SERIAL_COUNTER.next_serial(); match event.tip_state() { TabletToolTipState::Down => { - tool.tip_down(SERIAL_COUNTER.next_serial(), event.time_msec()); + tool.down( + self, + &tool::DownEvent { + serial, + time: event.time_msec(), + }, + ); } TabletToolTipState::Up => { - tool.tip_up(event.time_msec()); + tool.up( + self, + &tool::UpEvent { + serial, + time: event.time_msec(), + }, + ); } } + + tool.frame(self, event.time_msec()); } } } @@ -1567,11 +1609,16 @@ impl State { notify_cursor_activity(self, &seat); if let Some(tool) = seat.tablet_seat().get_tool(&event.tool()) { tool.button( - event.button(), - event.button_state(), - SERIAL_COUNTER.next_serial(), - event.time_msec(), + self, + &tool::ButtonEvent { + button: event.button(), + state: event.button_state(), + serial: SERIAL_COUNTER.next_serial(), + time: event.time_msec(), + }, ); + + tool.frame(self, event.time_msec()); } } } diff --git a/src/wayland/handlers/tablet_manager.rs b/src/wayland/handlers/tablet_manager.rs index 60469ba9..e4b84434 100644 --- a/src/wayland/handlers/tablet_manager.rs +++ b/src/wayland/handlers/tablet_manager.rs @@ -3,10 +3,12 @@ use crate::state::State; use smithay::{ backend::input::TabletToolDescriptor, input::pointer::CursorImageStatus, - wayland::tablet_manager::TabletSeatHandler, + input::tablet::TabletSeatHandler, reexports::wayland_server::protocol::wl_surface, }; impl TabletSeatHandler for State { + type ToolFocus = wl_surface::WlSurface; + fn tablet_tool_image(&mut self, _tool: &TabletToolDescriptor, _image: CursorImageStatus) { // TODO display cursor for each tablet tool } From 861b7326715ff529dc26de44704051c61496f87a Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Fri, 19 Jun 2026 18:38:58 -0700 Subject: [PATCH 18/80] input: Handle scroll events that only have `amount_v120` X11, winit, and libei backends all can produce scroll events with `amount_v120()` but no `amount()`. --- src/input/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index f323cf4a..d7dbeb20 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -968,7 +968,10 @@ impl State { } } else { let mut frame = AxisFrame::new(event.time_msec()).source(event.source()); - if let Some(horizontal_amount) = event.amount(Axis::Horizontal) { + let horizontal_amount = event + .amount(Axis::Horizontal) + .or_else(|| Some(event.amount_v120(Axis::Horizontal)? * 15.0 / 120.)); + if let Some(horizontal_amount) = horizontal_amount { if horizontal_amount != 0.0 { frame = frame .relative_direction( @@ -986,7 +989,10 @@ impl State { frame = frame.stop(Axis::Horizontal); } } - if let Some(vertical_amount) = event.amount(Axis::Vertical) { + let vertical_amount = event + .amount(Axis::Vertical) + .or_else(|| Some(event.amount_v120(Axis::Vertical)? * 15.0 / 120.)); + if let Some(vertical_amount) = vertical_amount { if vertical_amount != 0.0 { frame = frame .relative_direction( From e939a72b30078990ea7b4a91d6243ef98130c555 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Wed, 8 May 2024 21:16:10 -0700 Subject: [PATCH 19/80] Initial Ei protocol support --- Cargo.toml | 1 + src/lib.rs | 3 +++ src/libei.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 src/libei.rs diff --git a/Cargo.toml b/Cargo.toml index cfe97687..7d4ff3af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,6 +97,7 @@ features = [ "backend_drm", "backend_gbm", "backend_egl", + "backend_libei", "backend_libinput", "backend_session_libseat", "backend_udev", diff --git a/src/lib.rs b/src/lib.rs index f7282942..d0bf28b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,6 +43,7 @@ pub mod dbus; pub mod debug; pub mod hooks; pub mod input; +pub mod libei; mod logger; pub mod session; pub mod shell; @@ -179,6 +180,8 @@ pub fn run(hooks: crate::hooks::Hooks) -> Result<(), Box> { // init backend backend::init_backend_auto(&display, &mut event_loop, &mut state)?; + libei::listen_eis(&event_loop.handle()); + if let Err(err) = theme::watch_theme(event_loop.handle()) { warn!(?err, "Failed to watch theme"); } diff --git a/src/libei.rs b/src/libei.rs new file mode 100644 index 00000000..c0c1ed3e --- /dev/null +++ b/src/libei.rs @@ -0,0 +1,46 @@ +use reis::calloop::EisListenerSource; +use reis::eis; +use smithay::reexports::reis; + +use smithay::backend::libei::{EiInput, EiInputEvent}; +use smithay::input::keyboard::XkbConfig; +use smithay::reexports::calloop; + +use crate::state::State; + +pub fn listen_eis(handle: &calloop::LoopHandle<'static, State>) { + let listener = match eis::Listener::bind_auto() { + Ok(listener) => listener, + Err(err) => { + tracing::error!("Failed to bind EI listener socket: {}", err); + return; + } + }; + + unsafe { std::env::set_var("LIBEI_SOCKET", listener.path()) }; + + let listener_source = EisListenerSource::new(listener); + let handle_clone = handle.clone(); + handle + .insert_source(listener_source, move |context, _, _| { + let source = EiInput::new(context); + handle_clone + .insert_source(source, |event, connection, data| match event { + EiInputEvent::Connected => { + let seat = connection.add_seat("default"); + // TODO config + let _ = seat.add_keyboard("virtual keyboard", XkbConfig::default()); + seat.add_pointer("virtual pointer"); + seat.add_pointer_absolute("virtual absoulte pointer"); + seat.add_touch("virtual touch"); + } + EiInputEvent::Disconnected => {} + EiInputEvent::Event(event) => { + data.process_input_event(event); + } + }) + .unwrap(); + Ok(calloop::PostAction::Continue) + }) + .unwrap(); +} From f4ee52c46a7eae97042dc96d18419bf7796356bd Mon Sep 17 00:00:00 2001 From: Hojjat Date: Fri, 5 Jun 2026 10:42:19 -0600 Subject: [PATCH 20/80] Ei protocol support - Use the configured keyboard layout, track ei seats to reconfigure their keyboards - DBus interface - Disambiguate input devices per backend instance - Add support for `ei_keysym` and `ei_text` - Act as input method for ei_text UTF-8 injection - Create a region per output for abosolute pointer position - Map ei region for touchscreen emulation - Release pointer buttons and keys on removal - Send modifiers to EI connections --- src/backend/kms/mod.rs | 2 +- src/backend/winit.rs | 8 +- src/backend/x11.rs | 4 +- src/config/mod.rs | 22 + src/dbus/ei.rs | 79 ++ src/dbus/mod.rs | 9 + src/input/actions.rs | 23 +- src/input/mod.rs | 796 +++++++++++++++---- src/lib.rs | 6 +- src/libei.rs | 228 +++++- src/shell/layout/tiling/grabs/swap.rs | 2 + src/shell/seats.rs | 92 ++- src/state.rs | 25 + src/wayland/handlers/output_configuration.rs | 10 + src/xwayland.rs | 4 +- 15 files changed, 1092 insertions(+), 218 deletions(-) create mode 100644 src/dbus/ei.rs diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index df711387..6da04aac 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -211,7 +211,7 @@ fn init_libinput( state.backend.kms().input_devices.remove(&*device.name()); } - state.process_input_event(event); + state.process_input_event(event, crate::input::InputBackendId::Normal); for output in state.common.shell.read().outputs() { state.backend.kms().schedule_render(output); diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 24ef64b4..8f3d14c5 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -310,7 +310,9 @@ impl State { WinitEvent::Focus(true) => { for seat in self.common.shell.read().seats.iter() { let devices = seat.user_data().get::().unwrap(); - if devices.has_device(&WinitVirtualDevice) { + if devices + .has_device(&WinitVirtualDevice, &crate::input::InputBackendId::Normal) + { seat.set_active_output(&self.backend.winit().output); break; } @@ -340,7 +342,9 @@ impl State { render_ping.ping(); } WinitEvent::Redraw => render_ping.ping(), - WinitEvent::Input(event) => self.process_input_event(event), + WinitEvent::Input(event) => { + self.process_input_event(event, crate::input::InputBackendId::Normal) + } WinitEvent::CloseRequested => { self.common.should_stop = true; } diff --git a/src/backend/x11.rs b/src/backend/x11.rs index 98b27453..ed92e844 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -534,14 +534,14 @@ impl State { let device = event.device(); for seat in self.common.shell.read().seats.iter() { let devices = seat.user_data().get::().unwrap(); - if devices.has_device(&device) { + if devices.has_device(&device, &crate::input::InputBackendId::Normal) { seat.set_active_output(&output); break; } } }; - self.process_input_event(event); + self.process_input_event(event, crate::input::InputBackendId::Normal); // TODO actually figure out the output for output in self.common.shell.read().outputs() { self.backend.x11().schedule_render(output); diff --git a/src/config/mod.rs b/src/config/mod.rs index 1d9b9c92..4a2500fd 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ + input::InputBackendId, shell::Shell, state::{BackendData, State}, utils::prelude::OutputExt, @@ -820,6 +821,27 @@ fn config_changed(config: cosmic_config::Config, keys: Vec, state: &mut } } } + let ei_connections = state + .common + .ei_keyboard_source + .keys() + .cloned() + .collect::>(); + for conn in &ei_connections { + state.release_ei_keyboard(conn); + state.clear_input_source_state(&InputBackendId::Ei(conn.clone())); + } + for ei_seat in state.common.ei_seats.values() { + if let Err(err) = + ei_seat.add_keyboard("virtual keyboard", xkb_config_to_wl(&value)) + { + warn!(?err, "Failed to update libei keyboard keymap"); + } + } + if !state.common.ei_seats.is_empty() { + let seat = state.common.shell.read().seats.last_active().clone(); + state.broadcast_ei_keyboard_modifiers(&seat); + } state.common.config.cosmic_conf.xkb_config = value; } "keyboard_config" => { diff --git a/src/dbus/ei.rs b/src/dbus/ei.rs new file mode 100644 index 00000000..6391dc76 --- /dev/null +++ b/src/dbus/ei.rs @@ -0,0 +1,79 @@ +use std::{ + os::unix::net::UnixStream, + sync::{Arc, Mutex}, +}; + +use smithay::reexports::calloop; +use zbus::names::{UniqueName, WellKnownName}; + +use super::name_owners::NameOwners; + +static ALLOWED_NAMES: &[WellKnownName] = &[WellKnownName::from_static_str_unchecked( + "org.freedesktop.impl.portal.desktop.cosmic", +)]; + +/// Channel for handing the EI socketpair (and requested device types) +/// It's `None` until the EI sender side has been set up +type EiSender = Arc>>>; + +struct Ei { + ei_sender: EiSender, + name_owners: NameOwners, +} + +impl Ei { + async fn check_sender_allowed(&self, sender: &UniqueName<'_>) -> zbus::fdo::Result<()> { + if self.name_owners.check_owner(sender, ALLOWED_NAMES).await { + Ok(()) + } else { + Err(zbus::fdo::Error::AccessDenied("Access denied".to_string())) + } + } +} + +#[zbus::interface(name = "com.system76.CosmicComp.Ei")] +impl Ei { + /// Create a new EI sender context + async fn get_sender_socket( + &self, + device_types: u32, + #[zbus(header)] header: zbus::message::Header<'_>, + ) -> zbus::fdo::Result { + if let Some(sender) = header.sender() { + self.check_sender_allowed(sender).await?; + } + + let (comp_stream, client_stream) = UnixStream::pair().map_err(|err| { + zbus::fdo::Error::Failed(format!("Failed to create socket pair: {err}")) + })?; + + { + let guard = self.ei_sender.lock().unwrap(); + let sender = guard + .as_ref() + .ok_or_else(|| zbus::fdo::Error::Failed("EI sender not available".to_string()))?; + sender.send((comp_stream, device_types)).map_err(|err| { + zbus::fdo::Error::Failed(format!("Failed to hand off EI socket: {err}")) + })?; + } + + Ok(std::os::fd::OwnedFd::from(client_stream).into()) + } +} + +/// Register the `com.system76.CosmicComp.Ei` interface on the shared session connection. +pub async fn init( + conn: &zbus::Connection, + name_owners: &NameOwners, + ei_sender: EiSender, +) -> zbus::Result<()> { + let ei = Ei { + ei_sender, + name_owners: name_owners.clone(), + }; + conn.object_server() + .at("/com/system76/CosmicComp/Ei", ei) + .await?; + conn.request_name("com.system76.CosmicComp").await?; + Ok(()) +} diff --git a/src/dbus/mod.rs b/src/dbus/mod.rs index 5b9754c3..9138caee 100644 --- a/src/dbus/mod.rs +++ b/src/dbus/mod.rs @@ -9,11 +9,13 @@ use std::{ cell::{RefCell, RefMut}, collections::HashMap, rc::Rc, + sync::{Arc, Mutex}, }; use tracing::{error, warn}; pub mod a11y_keyboard_monitor; use a11y_keyboard_monitor::A11yKeyboardMonitorState; +pub mod ei; #[cfg(feature = "logind")] pub mod logind; mod name_owners; @@ -29,6 +31,7 @@ struct DBusStateInner { session_conn: zbus::Result, system_conn: zbus::Result, a11y_keyboard_monitor: RefCell>, + ei_sender: Arc>>>, } impl DBusState { @@ -42,6 +45,7 @@ impl DBusState { session_conn, system_conn, a11y_keyboard_monitor: RefCell::new(None), + ei_sender: Arc::new(Mutex::new(None)), })); evlh.insert_source(source, |_, _, _| {}).unwrap(); let state_clone = state.clone(); @@ -65,6 +69,10 @@ impl DBusState { RefMut::filter_map(self.0.a11y_keyboard_monitor.borrow_mut(), |x| x.as_mut()).ok() } + pub fn set_ei_sender(&self, sender: calloop::channel::Sender) { + *self.0.ei_sender.lock().unwrap() = Some(sender); + } + // 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.as_ref().map_err(|err| err.clone()) @@ -85,6 +93,7 @@ async fn init_session(state: &DBusState) -> zbus::Result<()> { 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); + ei::init(conn, &name_owners, state.0.ei_sender.clone()).await?; Ok(()) } diff --git a/src/input/actions.rs b/src/input/actions.rs index 3d017697..a4932597 100644 --- a/src/input/actions.rs +++ b/src/input/actions.rs @@ -2,6 +2,7 @@ use crate::{ config::{Action, PrivateAction}, + input::InputBackendId, shell::{ FocusResult, InvalidWorkspaceIndex, MoveResult, SeatExt, Trigger, WorkspaceDelta, focus::{FocusTarget, target::KeyboardFocusTarget}, @@ -39,6 +40,7 @@ impl State { pub fn handle_action( &mut self, action: Action, + backend_id: &InputBackendId, seat: &Seat, serial: Serial, time: u32, @@ -69,7 +71,7 @@ impl State { Action::Shortcut(action) => { let propagate = propagate_by_default(&action); self.handle_shortcut_action( - action, seat, serial, time, pattern, direction, propagate, + action, backend_id, seat, serial, time, pattern, direction, propagate, ) } Action::Private(PrivateAction::Escape) => { @@ -143,6 +145,7 @@ impl State { pub fn handle_shortcut_action( &mut self, action: shortcuts::Action, + backend_id: &InputBackendId, seat: &Seat, serial: Serial, time: u32, @@ -232,6 +235,7 @@ impl State { { self.handle_shortcut_action( Action::SwitchOutput(inferred), + backend_id, seat, serial, time, @@ -271,6 +275,7 @@ impl State { { self.handle_shortcut_action( Action::SwitchOutput(inferred), + backend_id, seat, serial, time, @@ -392,6 +397,7 @@ impl State { } else { Action::SendToOutput(inferred) }, + backend_id, seat, serial, time, @@ -417,6 +423,7 @@ impl State { } else { Action::SendToWorkspace(1) }, + backend_id, seat, serial, time, @@ -483,6 +490,7 @@ impl State { } else { Action::SendToOutput(inferred) }, + backend_id, seat, serial, time, @@ -508,6 +516,7 @@ impl State { } else { Action::SendToLastWorkspace }, + backend_id, seat, serial, time, @@ -532,7 +541,9 @@ impl State { if propagate && let Some((serial, prev_output, prev_idx)) = shell.previous_workspace_idx.take() - && seat.last_modifier_change().is_some_and(|s| s == serial) + && seat + .last_modifier_change_for(backend_id) + .is_some_and(|s| s == serial) && prev_output == current_output { let _ = shell.activate( @@ -705,6 +716,7 @@ impl State { if res.is_ok() { self.handle_shortcut_action( Action::SwitchOutput(direction), + backend_id, seat, serial, time, @@ -741,7 +753,8 @@ impl State { }; if let Some(direction) = dir { - if let Some(last_mod_serial) = seat.last_modifier_change() { + if let Some(last_mod_serial) = seat.last_modifier_change_for(backend_id) + { let mut shell = self.common.shell.write(); if !shell .previous_workspace_idx @@ -776,6 +789,7 @@ impl State { self.handle_shortcut_action( action, + backend_id, seat, serial, time, @@ -800,7 +814,7 @@ impl State { .move_current_element(direction, seat); match res { MoveResult::MoveFurther(_move_further) => { - if let Some(last_mod_serial) = seat.last_modifier_change() { + if let Some(last_mod_serial) = seat.last_modifier_change_for(backend_id) { let mut shell = self.common.shell.write(); if !shell .previous_workspace_idx @@ -834,6 +848,7 @@ impl State { self.handle_shortcut_action( action, + backend_id, seat, serial, time, diff --git a/src/input/mod.rs b/src/input/mod.rs index d7dbeb20..c93b3405 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -11,7 +11,7 @@ use crate::{ }, input::gestures::{GestureState, SwipeAction}, shell::{ - LastModifierChange, SeatExt, Trigger, + SeatExt, Trigger, focus::{ Stage, render_input_order, target::{KeyboardFocusTarget, PointerFocusTarget}, @@ -42,14 +42,14 @@ use smithay::{ backend::input::{ AbsolutePositionEvent, Axis, AxisRelativeDirection, AxisSource, Device, DeviceCapability, GestureBeginEvent, GestureEndEvent, GesturePinchUpdateEvent as _, - GestureSwipeUpdateEvent as _, InputBackend, InputEvent, KeyState, KeyboardKeyEvent, - PointerAxisEvent, ProximityState, TabletToolButtonEvent, TabletToolEvent, - TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState, TouchEvent, + GestureSwipeUpdateEvent as _, InputBackend, InputEvent, KeyState, PointerAxisEvent, + ProximityState, TabletToolButtonEvent, TabletToolEvent, TabletToolProximityEvent, + TabletToolTipEvent, TabletToolTipState, TouchEvent, }, desktop::{PopupKeyboardGrab, WindowSurfaceType, utils::under_from_surface_tree}, input::{ Seat, - keyboard::{FilterResult, KeysymHandle, ModifiersState}, + keyboard::{FilterResult, KeyboardSource, KeysymHandle, ModifiersState}, pointer::{ AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, @@ -64,7 +64,7 @@ use smithay::{ input::Device as InputDevice, wayland_server::{Resource as _, protocol::wl_surface::WlSurface}, }, - utils::{Clock, Logical, Monotonic, Point, Rectangle, SERIAL_COUNTER, Serial}, + utils::{Clock, Logical, Monotonic, Point, Rectangle, SERIAL_COUNTER, Serial, Size}, wayland::{ compositor::CompositorHandler, image_copy_capture::CursorSessionRef, @@ -80,7 +80,7 @@ use std::{ any::Any, borrow::Cow, cell::RefCell, - collections::HashSet, + collections::{HashMap, HashSet}, ops::ControlFlow, time::{Duration, Instant}, }; @@ -88,6 +88,19 @@ use std::{ pub mod actions; pub mod gestures; +/// Identifies the input backend instance an event came from, used to disambiguate device ids +/// (which are only unique within a single backend instance, see +/// [`smithay::backend::input::Device::id`]). +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum InputBackendId { + /// The session input backend (libinput / winit / x11) these are mutually exclusive + Normal, + /// A specific Ei client connection + Ei(smithay::reexports::reis::eis::Connection), + /// The `zwp_virtual_keyboard_v1` protocol (all virtual keyboards share this source) + VirtualKeyboard, +} + /// Used for debouncing focus updates due to pointer motion, if after the focus change is /// triggered the event will cancel if the pointer moves to the original target #[derive(Debug)] @@ -100,19 +113,35 @@ pub struct PointerFocusState { } #[derive(Default)] -pub struct SupressedKeys(RefCell)>>); +pub struct SupressedKeys( + RefCell)>>>, +); #[derive(Default)] -pub struct SupressedButtons(RefCell>); +pub struct SupressedButtons(RefCell>>); #[derive(Default, Debug)] -pub struct ModifiersShortcutQueue(RefCell>); +pub struct ModifiersShortcutQueue(RefCell>); impl SupressedKeys { - fn add(&self, keysym: &KeysymHandle, token: impl Into>) { - self.0.borrow_mut().push((keysym.raw_code(), token.into())); + fn add( + &self, + backend_id: &InputBackendId, + keysym: &KeysymHandle, + token: impl Into>, + ) { + self.0 + .borrow_mut() + .entry(backend_id.clone()) + .or_default() + .push((keysym.raw_code(), token.into())); } - fn filter(&self, keysym: &KeysymHandle) -> Option> { - let mut keys = self.0.borrow_mut(); + fn filter( + &self, + backend_id: &InputBackendId, + keysym: &KeysymHandle, + ) -> Option> { + let mut by_source = self.0.borrow_mut(); + let keys = by_source.get_mut(backend_id)?; let (removed, remaining) = keys .drain(..) .partition(|(key, _)| *key == keysym.raw_code()); @@ -129,44 +158,60 @@ impl SupressedKeys { .collect::>(), ) } + + fn clear_source(&self, backend_id: &InputBackendId) { + self.0.borrow_mut().remove(backend_id); + } } impl SupressedButtons { - fn add(&self, button: u32) { - self.0.borrow_mut().insert(button); + fn add(&self, backend_id: &InputBackendId, button: u32) { + self.0 + .borrow_mut() + .entry(backend_id.clone()) + .or_default() + .insert(button); } - fn remove(&self, button: u32) -> bool { - self.0.borrow_mut().remove(&button) + fn remove(&self, backend_id: &InputBackendId, button: u32) -> bool { + self.0 + .borrow_mut() + .get_mut(backend_id) + .is_some_and(|buttons| buttons.remove(&button)) + } + + fn clear_source(&self, backend_id: &InputBackendId) { + self.0.borrow_mut().remove(backend_id); } } impl ModifiersShortcutQueue { - pub fn set(&self, binding: shortcuts::Binding) { - let mut set = self.0.borrow_mut(); - *set = Some(binding); + pub fn set(&self, backend_id: &InputBackendId, binding: shortcuts::Binding) { + self.0.borrow_mut().insert(backend_id.clone(), binding); } - pub fn take(&self, binding: &shortcuts::Binding) -> bool { + pub fn take(&self, backend_id: &InputBackendId, binding: &shortcuts::Binding) -> bool { let mut set = self.0.borrow_mut(); - if set.is_some() && set.as_ref().unwrap() == binding { - *set = None; + if set.get(backend_id).is_some_and(|queued| queued == binding) { + set.remove(backend_id); true } else { false } } - pub fn clear(&self) { - let mut set = self.0.borrow_mut(); - *set = None; + pub fn clear(&self, backend_id: &InputBackendId) { + self.0.borrow_mut().remove(backend_id); } } impl State { #[profiling::function] - pub fn process_input_event(&mut self, event: InputEvent) - where + pub fn process_input_event( + &mut self, + event: InputEvent, + backend_id: InputBackendId, + ) where ::Device: 'static, { crate::wayland::handlers::output_power::set_all_surfaces_dpms_on(self); @@ -177,19 +222,25 @@ impl State { let shell = self.common.shell.read(); let seat = shell.seats.last_active(); let led_state = seat.get_keyboard().unwrap().led_state(); - seat.devices().add_device(&device, led_state); + seat.devices().add_device(&device, led_state, &backend_id); if device.has_capability(DeviceCapability::TabletTool) { seat.tablet_seat().add_wp_tablet( &self.common.display_handle, &TabletDescriptor::from(&device), ); } + let has_keyboard = device.has_capability(DeviceCapability::Keyboard); + std::mem::drop(shell); + // send the seat's current modifier state to the new ei keyboard + if has_keyboard && let InputBackendId::Ei(conn) = &backend_id { + self.send_ei_keyboard_modifiers(conn); + } } InputEvent::DeviceRemoved { device } => { for seat in &mut self.common.shell.read().seats.iter() { let devices = seat.devices(); - if devices.has_device(&device) { - devices.remove_device(&device); + if devices.has_device(&device, &backend_id) { + devices.remove_device(&device, &backend_id); if device.has_capability(DeviceCapability::TabletTool) { seat.tablet_seat() .remove_tablet(&TabletDescriptor::from(&device)); @@ -210,7 +261,7 @@ impl State { .shell .read() .seats - .for_device(&event.device()) + .for_device(&event.device(), &backend_id) .cloned(); if let Some(seat) = maybe_seat { self.common.idle_notifier_state.notify_activity(&seat); @@ -231,51 +282,17 @@ impl State { serial, time, |data, modifiers, handle| { - if previous_modifiers != *modifiers { - *seat - .user_data() - .get::() - .unwrap() - .0 - .lock() - .unwrap() = Some(serial); - } - - let current_focus = seat.get_keyboard().unwrap().current_focus(); - let shortcuts_inhibited = current_focus.as_ref().is_some_and(|f| { - f.wl_surface() - .map(|surface| { - seat.keyboard_shortcuts_inhibitor_for_surface(&surface) - .map(|inhibitor| inhibitor.is_active()) - .unwrap_or(false) - || seat.has_active_xwayland_grab(&surface) - }) - .unwrap_or(false) - }); - let sym = handle.modified_sym(); - - let result = Self::filter_keyboard_input( - data, &event, &seat, modifiers, handle, serial, - ); - - if (matches!(result, FilterResult::Forward) - && !seat.get_keyboard().unwrap().is_grabbed() - && !shortcuts_inhibited - && !matches!( - current_focus, - Some(KeyboardFocusTarget::LockSurface(_)) - )) - // we don't want to accidentally leave any keys pressed - // and do more filtering in `xwayland_notify_key_event` - // for released keys - || state == KeyState::Released - { - data.common.xwayland_notify_key_event( - sym, keycode, state, serial, time, - ); - } - - result + data.process_keyboard_filter( + &backend_id, + &seat, + modifiers, + handle, + serial, + time, + keycode, + state, + previous_modifiers, + ) }, ) .flatten() @@ -286,7 +303,7 @@ impl State { FilterResult::<()>::Forward }); } - self.handle_action(action, &seat, serial, time, pattern, None) + self.handle_action(action, &backend_id, &seat, serial, time, pattern, None) } // If we want to track numlock state so it can be reused on the next boot... @@ -311,7 +328,11 @@ impl State { use smithay::backend::input::PointerMotionEvent; let shell = self.common.shell.write(); - if let Some(seat) = shell.seats.for_device(&event.device()).cloned() { + if let Some(seat) = shell + .seats + .for_device(&event.device(), &backend_id) + .cloned() + { self.common.idle_notifier_state.notify_activity(&seat); notify_cursor_activity(self, &seat); let current_output = seat.active_output(); @@ -665,19 +686,47 @@ impl State { .shell .read() .seats - .for_device(&event.device()) + .for_device(&event.device(), &backend_id) .cloned(); if let Some(seat) = maybe_seat { self.common.idle_notifier_state.notify_activity(&seat); notify_cursor_activity(self, &seat); - let output = seat.active_output(); - let output_geometry = output.geometry(); - let position = output_geometry.loc.to_f64() - + smithay::backend::input::AbsolutePositionEvent::position_transformed( - &event, - output_geometry.size.as_logical(), - ) - .as_global(); + let (output, position) = if matches!(&backend_id, InputBackendId::Ei(_)) { + // EI absolute coordinates are in the compositor's *global* + // logical space: each advertised region carries its output's + // global offset, so the client sends a global position. Use + // the coordinate directly and find the output it lands in, + // rather than mapping relative to the focused output (which + // cannot address other monitors). This is the KWin/mutter + // model. + let position = + smithay::backend::input::AbsolutePositionEvent::position_transformed( + &event, + // smithay's EI impl ignores the size and returns the + // raw coordinate. + Size::from((0, 0)), + ) + .as_global(); + let output = self + .common + .shell + .read() + .outputs() + .find(|o| o.geometry().to_f64().contains(position)) + .cloned() + .unwrap_or_else(|| seat.active_output()); + (output, position) + } else { + let output = seat.active_output(); + let output_geometry = output.geometry(); + let position = output_geometry.loc.to_f64() + + smithay::backend::input::AbsolutePositionEvent::position_transformed( + &event, + output_geometry.size.as_logical(), + ) + .as_global(); + (output, position) + }; let serial = SERIAL_COUNTER.next_serial(); let under = State::surface_under(position, &output, &self.common.shell.write()) .map(|(target, pos)| (target, pos.as_logical())); @@ -694,6 +743,14 @@ impl State { ); ptr.frame(self); + // Keep the seat's active output following the pointer. Click-to- + // focus (PointerButton) resolves its target via + // `seat.active_output()` + let previous_output = seat.active_output(); + if previous_output != output { + seat.set_active_output(&output); + } + let shell = self.common.shell.read(); update_output_image_copy_cursor_position( &shell, @@ -713,7 +770,7 @@ impl State { .shell .read() .seats - .for_device(&event.device()) + .for_device(&event.device(), &backend_id) .cloned() else { return; @@ -736,7 +793,26 @@ impl State { let serial = SERIAL_COUNTER.next_serial(); let button = event.button_code(); - let mut pass_event = !seat.supressed_buttons().remove(button); + // Track buttons held by a libei source so they can be released if the connection + // drops mid-press + if let InputBackendId::Ei(conn) = &backend_id { + match event.state() { + ButtonState::Pressed => { + self.common + .ei_pointer_buttons + .entry(conn.clone()) + .or_default() + .insert(button); + } + ButtonState::Released => { + if let Some(held) = self.common.ei_pointer_buttons.get_mut(conn) { + held.remove(&button); + } + } + } + } + + let mut pass_event = !seat.supressed_buttons().remove(&backend_id, button); if event.state() == ButtonState::Pressed { // change the keyboard focus unless the pointer is grabbed // We test for any matching surface type here but always use the root @@ -758,7 +834,7 @@ impl State { ); if let Some(target) = under.filter(|_| !on_resize_fork) { if let Some(surface) = target.toplevel().map(Cow::into_owned) - && seat.get_keyboard().unwrap().modifier_state().logo + && self.source_modifiers(&backend_id, &seat).logo && !shortcuts_inhibited { let seat_clone = seat.clone(); @@ -769,17 +845,18 @@ impl State { // aimed at the compositor and shouldn't be passed // to the application. pass_event = false; - seat.supressed_buttons().add(button); + seat.supressed_buttons().add(&backend_id, button); }; fn dispatch_grab + 'static>( grab: Option<(G, smithay::input::pointer::Focus)>, seat: Seat, + backend_id: &InputBackendId, serial: Serial, state: &mut State, ) { if let Some((target, focus)) = grab { - seat.modifiers_shortcut_queue().clear(); + seat.modifiers_shortcut_queue().clear(backend_id); seat.get_pointer() .unwrap() @@ -791,6 +868,7 @@ impl State { match mouse_button { smithay::backend::input::MouseButton::Left => { supress_button(); + let backend_id = backend_id.clone(); self.common.event_loop_handle.insert_idle( move |state| { let mut shell = state.common.shell.write(); @@ -805,12 +883,19 @@ impl State { false, ); drop(shell); - dispatch_grab(res, seat_clone, serial, state); + dispatch_grab( + res, + seat_clone, + &backend_id, + serial, + state, + ); }, ); } smithay::backend::input::MouseButton::Right => { supress_button(); + let backend_id = backend_id.clone(); self.common.event_loop_handle.insert_idle( move |state| { let mut shell = state.common.shell.write(); @@ -868,7 +953,13 @@ impl State { false, ); drop(shell); - dispatch_grab(res, seat_clone, serial, state); + dispatch_grab( + res, + seat_clone, + &backend_id, + serial, + state, + ); }, ); } @@ -932,13 +1023,13 @@ impl State { .shell .read() .seats - .for_device(&event.device()) + .for_device(&event.device(), &backend_id) .cloned(); if let Some(seat) = maybe_seat { self.common.idle_notifier_state.notify_activity(&seat); notify_cursor_activity(self, &seat); - if seat.get_keyboard().unwrap().modifier_state().logo + if self.source_modifiers(&backend_id, &seat).logo && self .common .config @@ -946,7 +1037,7 @@ impl State { .accessibility_zoom .enable_mouse_zoom_shortcuts { - seat.modifiers_shortcut_queue().clear(); + seat.modifiers_shortcut_queue().clear(&backend_id); if let Some(mut percentage) = event .amount_v120(Axis::Vertical) .map(|val| val / 120.) @@ -1023,7 +1114,7 @@ impl State { .shell .read() .seats - .for_device(&event.device()) + .for_device(&event.device(), &backend_id) .cloned(); if let Some(seat) = maybe_seat { self.common.idle_notifier_state.notify_activity(&seat); @@ -1049,7 +1140,7 @@ impl State { .shell .read() .seats - .for_device(&event.device()) + .for_device(&event.device(), &backend_id) .cloned(); if let Some(seat) = maybe_seat { self.common.idle_notifier_state.notify_activity(&seat); @@ -1150,7 +1241,7 @@ impl State { .shell .read() .seats - .for_device(&event.device()) + .for_device(&event.device(), &backend_id) .cloned(); if let Some(seat) = maybe_seat { self.common.idle_notifier_state.notify_activity(&seat); @@ -1195,7 +1286,7 @@ impl State { .shell .read() .seats - .for_device(&event.device()) + .for_device(&event.device(), &backend_id) .cloned(); if let Some(seat) = maybe_seat { self.common.idle_notifier_state.notify_activity(&seat); @@ -1217,7 +1308,7 @@ impl State { .shell .read() .seats - .for_device(&event.device()) + .for_device(&event.device(), &backend_id) .cloned(); if let Some(seat) = maybe_seat { self.common.idle_notifier_state.notify_activity(&seat); @@ -1239,7 +1330,7 @@ impl State { .shell .read() .seats - .for_device(&event.device()) + .for_device(&event.device(), &backend_id) .cloned(); if let Some(seat) = maybe_seat { self.common.idle_notifier_state.notify_activity(&seat); @@ -1261,7 +1352,7 @@ impl State { .shell .read() .seats - .for_device(&event.device()) + .for_device(&event.device(), &backend_id) .cloned(); if let Some(seat) = maybe_seat { self.common.idle_notifier_state.notify_activity(&seat); @@ -1283,7 +1374,7 @@ impl State { .shell .read() .seats - .for_device(&event.device()) + .for_device(&event.device(), &backend_id) .cloned(); if let Some(seat) = maybe_seat { self.common.idle_notifier_state.notify_activity(&seat); @@ -1302,17 +1393,40 @@ impl State { InputEvent::TouchDown { event, .. } => { let shell = self.common.shell.write(); - if let Some(seat) = shell.seats.for_device(&event.device()).cloned() { + if let Some(seat) = shell + .seats + .for_device(&event.device(), &backend_id) + .cloned() + { self.common.idle_notifier_state.notify_activity(&seat); - let Some(output) = - mapped_output_for_device(&self.common.config, &shell, &event.device()) + // Check if the touch is from an ei device or a mapped device + // EI absolute coordinates are already in the compositor's global logical + // space (each advertised region carries its output's global offset), so use + // them directly and find the output they land in. + let (output, position) = if matches!(&backend_id, InputBackendId::Ei(_)) { + let position = + smithay::backend::input::AbsolutePositionEvent::position_transformed( + &event, + Size::from((0, 0)), + ) + .as_global(); + let output = shell + .outputs() + .find(|o| o.geometry().to_f64().contains(position)) .cloned() - else { - return; + .unwrap_or_else(|| seat.active_output()); + (output, position) + } else { + let Some(output) = + mapped_output_for_device(&self.common.config, &shell, &event.device()) + .cloned() + else { + return; + }; + let position = + transform_output_mapped_position(&output, &event, shell.zoom_state()); + (output, position) }; - - let position = - transform_output_mapped_position(&output, &event, shell.zoom_state()); let under = State::surface_under(position, &output, &shell) .map(|(target, pos)| (target, pos.as_logical())); @@ -1334,17 +1448,36 @@ impl State { } InputEvent::TouchMotion { event, .. } => { let shell = self.common.shell.write(); - if let Some(seat) = shell.seats.for_device(&event.device()).cloned() { + if let Some(seat) = shell + .seats + .for_device(&event.device(), &backend_id) + .cloned() + { self.common.idle_notifier_state.notify_activity(&seat); - let Some(output) = - mapped_output_for_device(&self.common.config, &shell, &event.device()) + let (output, position) = if matches!(&backend_id, InputBackendId::Ei(_)) { + let position = + smithay::backend::input::AbsolutePositionEvent::position_transformed( + &event, + Size::from((0, 0)), + ) + .as_global(); + let output = shell + .outputs() + .find(|o| o.geometry().to_f64().contains(position)) .cloned() - else { - return; + .unwrap_or_else(|| seat.active_output()); + (output, position) + } else { + let Some(output) = + mapped_output_for_device(&self.common.config, &shell, &event.device()) + .cloned() + else { + return; + }; + let position = + transform_output_mapped_position(&output, &event, shell.zoom_state()); + (output, position) }; - - let position = - transform_output_mapped_position(&output, &event, shell.zoom_state()); let under = State::surface_under(position, &output, &shell) .map(|(target, pos)| (target, pos.as_logical())); @@ -1370,7 +1503,10 @@ impl State { shell.set_overview_mode(None, self.common.event_loop_handle.clone()); } - let maybe_seat = shell.seats.for_device(&event.device()).cloned(); + let maybe_seat = shell + .seats + .for_device(&event.device(), &backend_id) + .cloned(); if let Some(seat) = maybe_seat { self.common.idle_notifier_state.notify_activity(&seat); std::mem::drop(shell); @@ -1392,7 +1528,7 @@ impl State { .shell .read() .seats - .for_device(&event.device()) + .for_device(&event.device(), &backend_id) .cloned(); if let Some(seat) = maybe_seat { self.common.idle_notifier_state.notify_activity(&seat); @@ -1406,7 +1542,7 @@ impl State { .shell .read() .seats - .for_device(&event.device()) + .for_device(&event.device(), &backend_id) .cloned(); if let Some(seat) = maybe_seat { self.common.idle_notifier_state.notify_activity(&seat); @@ -1417,7 +1553,11 @@ impl State { InputEvent::TabletToolAxis { event, .. } => { let shell = self.common.shell.write(); - if let Some(seat) = shell.seats.for_device(&event.device()).cloned() { + if let Some(seat) = shell + .seats + .for_device(&event.device(), &backend_id) + .cloned() + { self.common.idle_notifier_state.notify_activity(&seat); notify_cursor_activity(self, &seat); let Some(output) = @@ -1482,7 +1622,11 @@ impl State { } InputEvent::TabletToolProximity { event, .. } => { let shell = self.common.shell.write(); - if let Some(seat) = shell.seats.for_device(&event.device()).cloned() { + if let Some(seat) = shell + .seats + .for_device(&event.device(), &backend_id) + .cloned() + { self.common.idle_notifier_state.notify_activity(&seat); notify_cursor_activity(self, &seat); let Some(output) = @@ -1570,7 +1714,7 @@ impl State { .shell .read() .seats - .for_device(&event.device()) + .for_device(&event.device(), &backend_id) .cloned(); if let Some(seat) = maybe_seat { self.common.idle_notifier_state.notify_activity(&seat); @@ -1608,7 +1752,7 @@ impl State { .shell .read() .seats - .for_device(&event.device()) + .for_device(&event.device(), &backend_id) .cloned(); if let Some(seat) = maybe_seat { self.common.idle_notifier_state.notify_activity(&seat); @@ -1670,15 +1814,335 @@ impl State { } } + /// The modifier state held by the source that produced an event. + pub(crate) fn source_modifiers( + &self, + _backend_id: &InputBackendId, + seat: &Seat, + ) -> ModifiersState { + seat.get_keyboard() + .map(|k| k.modifier_state()) + .unwrap_or_default() + } + + pub(crate) fn clear_input_source_state(&mut self, backend_id: &InputBackendId) { + let seats = self + .common + .shell + .read() + .seats + .iter() + .cloned() + .collect::>(); + for seat in seats { + seat.supressed_keys().clear_source(backend_id); + seat.supressed_buttons().clear_source(backend_id); + seat.modifiers_shortcut_queue().clear(backend_id); + seat.clear_last_modifier_change(backend_id); + } + } + + /// Release the keys this libei connection still holds on the shared seat, so they don't stay + /// stuck in the focused client. Keeps the connection's source valid (only clears held keys), + /// so it's safe to call both on disconnect and on a keymap change. Does not remove the source + /// from [`Common::ei_keyboard_source`], the disconnect path does that. + pub(crate) fn release_ei_keyboard(&mut self, conn: &smithay::reexports::reis::eis::Connection) { + let seat = self.common.shell.read().seats.last_active().clone(); + let Some(keyboard) = seat.get_keyboard() else { + return; + }; + if let Some(source) = self.common.ei_keyboard_source.get(conn).copied() { + keyboard.release_source(self, source); + } + } + + /// Release any pointer buttons this libei connection still holds + pub(crate) fn release_ei_pointer(&mut self, conn: &smithay::reexports::reis::eis::Connection) { + let buttons: Vec = self + .common + .ei_pointer_buttons + .get(conn) + .map(|held| held.iter().copied().collect()) + .unwrap_or_default(); + if buttons.is_empty() { + return; + } + let seat = self.common.shell.read().seats.last_active().clone(); + let Some(pointer) = seat.get_pointer() else { + return; + }; + let time = self.common.clock.now().as_millis(); + for button in buttons { + let serial = SERIAL_COUNTER.next_serial(); + pointer.button( + self, + &smithay::input::pointer::ButtonEvent { + button, + state: smithay::backend::input::ButtonState::Released, + serial, + time, + }, + ); + } + pointer.frame(self); + } + + /// Mirror the seat's current modifier state to every libei sender with a keyboard via + /// `ei_keyboard.modifiers` + pub(crate) fn broadcast_ei_keyboard_modifiers(&self, seat: &Seat) { + if self.common.ei_seats.is_empty() { + return; + } + let Some(keyboard) = seat.get_keyboard() else { + return; + }; + let s = keyboard.modifier_state().serialized; + for ei_seat in self.common.ei_seats.values() { + ei_seat.keyboard_modifiers(s.depressed, s.locked, s.latched, s.layout_effective); + } + } + + /// Send the seat's current modifier state to a single libei connection, used when that + /// connection's `ei_keyboard` device is created. The EI spec expects the current (nonzero) + /// modifier state to be announced once the device is live, so the client doesn't have to + /// wait for the next change to learn e.g. that Caps Lock is on. + pub(crate) fn send_ei_keyboard_modifiers( + &self, + conn: &smithay::reexports::reis::eis::Connection, + ) { + let Some(ei_seat) = self.common.ei_seats.get(conn) else { + return; + }; + let mods = { + let shell = self.common.shell.read(); + shell + .seats + .last_active() + .get_keyboard() + .map(|keyboard| keyboard.modifier_state().serialized) + }; + if let Some(s) = mods { + ei_seat.keyboard_modifiers(s.depressed, s.locked, s.latched, s.layout_effective); + } + } + + pub(crate) fn process_keyboard_filter( + &mut self, + backend_id: &InputBackendId, + seat: &Seat, + modifiers: &ModifiersState, + handle: KeysymHandle<'_>, + serial: Serial, + time: u32, + keycode: Keycode, + key_state: KeyState, + previous_modifiers: ModifiersState, + ) -> FilterResult> { + if previous_modifiers != *modifiers { + seat.set_last_modifier_change(backend_id, serial); + self.broadcast_ei_keyboard_modifiers(seat); + } + + let current_focus = seat.get_keyboard().unwrap().current_focus(); + let shortcuts_inhibited = current_focus.as_ref().is_some_and(|f| { + f.wl_surface() + .map(|surface| { + seat.keyboard_shortcuts_inhibitor_for_surface(&surface) + .map(|inhibitor| inhibitor.is_active()) + .unwrap_or(false) + || seat.has_active_xwayland_grab(&surface) + }) + .unwrap_or(false) + }); + let sym = handle.modified_sym(); + + let result = self.filter_keyboard_input( + backend_id, seat, modifiers, handle, serial, keycode, key_state, time, + ); + + if (matches!(result, FilterResult::Forward) + && !seat.get_keyboard().unwrap().is_grabbed() + && !shortcuts_inhibited + && !matches!(current_focus, Some(KeyboardFocusTarget::LockSurface(_)))) + // we don't want to accidentally leave any keys pressed + || key_state == KeyState::Released + { + self.common + .xwayland_notify_key_event(sym, keycode, key_state, *modifiers, serial, time); + } + + result + } + + /// Inject a key from an auxiliary source (a virtual keyboard, or libei) into the shared + /// seat keyboard, tagged with `source` so its held keys are tracked independently of the + /// physical keyboard + /// + /// `handle_shortcuts` is `false` when synthesizing releases so still-held keys are just + /// forwarded without re-triggering bindings. + pub(crate) fn inject_source_key( + &mut self, + source: KeyboardSource, + backend_id: &InputBackendId, + seat: &Seat, + keycode: Keycode, + key_state: KeyState, + handle_shortcuts: bool, + ) { + let Some(keyboard) = seat.get_keyboard() else { + return; + }; + let serial = SERIAL_COUNTER.next_serial(); + let time = self.common.clock.now().as_millis(); + let previous_modifiers = keyboard.modifier_state(); + let result = keyboard + .input_from_source( + source, + self, + keycode, + key_state, + serial, + time, + |data, modifiers, handle| { + if handle_shortcuts { + data.process_keyboard_filter( + backend_id, + seat, + modifiers, + handle, + serial, + time, + keycode, + key_state, + previous_modifiers, + ) + } else { + FilterResult::Forward + } + }, + ) + .flatten(); + + if let Some((action, pattern)) = result { + self.handle_action(action, backend_id, seat, serial, time, pattern, None); + } + } + + /// Inject a real key from a libei connection's `ei_keyboard` into the shared seat keyboard, + /// tagged with the connection's source so it behaves like any other keyboard (its keycodes + /// are interpreted with the seat keymap, which the compositor already forced onto the + /// `ei_keyboard` device). + pub(crate) fn inject_ei_key( + &mut self, + conn: &smithay::reexports::reis::eis::Connection, + keycode: Keycode, + key_state: KeyState, + ) { + let seat = self.common.shell.read().seats.last_active().clone(); + let backend_id = InputBackendId::Ei(conn.clone()); + let Some(source) = self.common.ei_keyboard_source.get(conn).copied() else { + return; + }; + self.inject_source_key(source, &backend_id, &seat, keycode, key_state, true); + } + + /// Inject a keysym from a libei `ei_text` device. + pub(crate) fn inject_ei_text_keysym( + &mut self, + conn: &smithay::reexports::reis::eis::Connection, + keysym: u32, + key_state: KeyState, + ) { + use smithay::wayland::input_method::InputMethodSeat; + use smithay::wayland::text_input::TextInputSeat; + + let keysym = Keysym::new(keysym); + + // `ei_text` is a tap: act on the press, ignore the release. + if key_state != KeyState::Pressed { + return; + } + let seat = self.common.shell.read().seats.last_active().clone(); + let Some(keyboard) = seat.get_keyboard() else { + return; + }; + + let mods = keyboard.modifier_state(); + let no_mods = !(mods.ctrl || mods.alt || mods.shift || mods.logo); + + // Is this keysym plain text, or not (Escape, F-keys, arrows, ...)? + let is_text = keysym.key_char().is_some_and(|c| !c.is_control()); + + // Resolve to a real keycode and feed it through the shared seat when this is *not* plain + // text, or when a modifier is held. + if !is_text || !no_mods { + if let Some(keycode) = keyboard.keycode_for_keysym(keysym) + && let Some(source) = self.common.ei_keyboard_source.get(conn).copied() + { + let backend_id = InputBackendId::Ei(conn.clone()); + self.inject_source_key( + source, + &backend_id, + &seat, + keycode, + KeyState::Pressed, + true, + ); + self.inject_source_key( + source, + &backend_id, + &seat, + keycode, + KeyState::Released, + true, + ); + return; + } + // No keycode for this keysym in the seat keymap (out-of-layout / Unicode), so fall + // through to the text paths below. Any held modifier is lost — best effort. + tracing::warn!( + "[ei-text] -> keysym not in seat keymap; falling back (modifier not applied)" + ); + } + + // No modifier held: a printable, non-control character committed directly through the + // text-input protocol when a text-input client is focused (and no real IME). This also + // covers out-of-layout / Unicode that has no keycode. + if no_mods + && is_text + && let Some(c) = keysym.key_char() + && !seat.input_method().has_instance() + { + let text_input = seat.text_input(); + let mut handled = false; + text_input.with_active_text_input(|ti, _surface| { + ti.commit_string(Some(c.to_string())); + handled = true; + }); + if handled { + text_input.done(false); + return; + } + } + + // Otherwise inject the keysym as a keycode tap via a temporary keymap delivered to just + // the focused client (reaches non-text-input apps: terminals, games, ...). This never + // touches the seat's own keyboard state. + keyboard.inject_text_keysyms(self, &[keysym]); + } + /// Determine is key event should be intercepted as a key binding, or forwarded to surface #[profiling::function] - pub fn filter_keyboard_input>( + pub fn filter_keyboard_input( &mut self, - event: &E, + backend_id: &InputBackendId, seat: &Seat, modifiers: &ModifiersState, handle: KeysymHandle<'_>, serial: Serial, + keycode: Keycode, + key_state: KeyState, + time: u32, ) -> FilterResult> { // Pre-compute for layout-agnostic shortcut matching let raw_syms = handle.raw_syms(); @@ -1696,7 +2160,12 @@ impl State { let keyboard_grabbed = keyboard.with_grab(|_serial, grab| { grab.is::() || grab.is::>() }) == Some(true); - let is_grabbed = keyboard_grabbed || pointer.is_grabbed(); + // A virtual-keyboard key can arrive while the seat's pointer is grabbed by that + // same on-screen keyboard's own button press (the implicit grab from clicking an OSK + // key). That pointer grab must not capture the injected key, otherwise e.g. + // pressing esc on a virtual keyboard gets swallowed here + let from_vk = matches!(backend_id, InputBackendId::VirtualKeyboard); + let is_grabbed = keyboard_grabbed || (pointer.is_grabbed() && !from_vk); let current_focus = keyboard.current_focus(); //this should fall back to active output since there may not be a focused output @@ -1714,7 +2183,7 @@ impl State { }); if let Some(a11y_keyboard_monitor) = self.common.dbus_state.a11y_keyboard_monitor() { - a11y_keyboard_monitor.key_event(modifiers, &handle, event.state()); + a11y_keyboard_monitor.key_event(modifiers, &handle, key_state); } // Leave move overview mode, if any modifier was released @@ -1736,7 +2205,7 @@ impl State { || (action_pattern.modifiers.shift && !modifiers.shift) || (action_pattern.key.is_some() && key_matches(action_pattern.key.unwrap()) - && event.state() == KeyState::Released)) + && key_state == KeyState::Released)) { shell.set_overview_mode(None, self.common.event_loop_handle.clone()); @@ -1752,7 +2221,7 @@ impl State { // Leave or update resize mode, if modifiers changed or initial key was released if let Some(action_pattern) = shell.resize_mode().0.active_binding() { if action_pattern.key.is_some() - && event.state() == KeyState::Released + && key_state == KeyState::Released && key_matches(action_pattern.key.unwrap()) { shell.set_resize_mode( @@ -1805,7 +2274,7 @@ impl State { let action = Action::Private(PrivateAction::Resizing( direction, edge.into(), - cosmic_keystate_from_smithay(event.state()), + cosmic_keystate_from_smithay(key_state), )); let key_pattern = shortcuts::Binding { modifiers: cosmic_modifiers_from_smithay(*modifiers), @@ -1814,8 +2283,8 @@ impl State { description: None, }; - if event.state() == KeyState::Released { - if let Some(tokens) = seat.supressed_keys().filter(&handle) { + if key_state == KeyState::Released { + if let Some(tokens) = seat.supressed_keys().filter(backend_id, &handle) { for token in tokens { self.common.event_loop_handle.remove(token); } @@ -1824,8 +2293,8 @@ impl State { let seat_clone = seat.clone(); let action_clone = action.clone(); let key_pattern_clone = key_pattern.clone(); + let backend_id_clone = backend_id.clone(); let start = Instant::now(); - let time = event.time_msec(); let token = self .common .event_loop_handle @@ -1835,6 +2304,7 @@ impl State { let duration = current.duration_since(start).as_millis(); state.handle_action( action_clone.clone(), + &backend_id_clone, &seat_clone, serial, time.overflowing_add(duration as u32).0, @@ -1846,7 +2316,7 @@ impl State { ) .ok(); - seat.supressed_keys().add(&handle, token); + seat.supressed_keys().add(backend_id, &handle, token); } return FilterResult::Intercept(Some((action, key_pattern))); } @@ -1857,13 +2327,13 @@ impl State { // cancel grabs if is_grabbed && handle.modified_sym() == Keysym::Escape - && event.state() == KeyState::Pressed + && key_state == KeyState::Pressed && !modifiers.alt && !modifiers.ctrl && !modifiers.logo && !modifiers.shift { - seat.supressed_keys().add(&handle, None); + seat.supressed_keys().add(backend_id, &handle, None); return FilterResult::Intercept(Some(( Action::Private(PrivateAction::Escape), shortcuts::Binding { @@ -1876,7 +2346,7 @@ impl State { } if let Some(mut a11y_keyboard_monitor) = self.common.dbus_state.a11y_keyboard_monitor() { - if event.state() == KeyState::Released { + if key_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 @@ -1885,7 +2355,7 @@ impl State { && (modifiers.serialized.locked & 2) != 0 { let seat = seat.clone(); - let key_code = event.key_code(); + let key_code = keycode; self.common.event_loop_handle.insert_idle(move |state| { if let Some(keyboard) = seat.get_keyboard() { let serial = SERIAL_COUNTER.next_serial(); @@ -1910,7 +2380,7 @@ impl State { } }); } - } else if event.state() == KeyState::Pressed + } else if key_state == KeyState::Pressed && a11y_keyboard_monitor.has_virtual_mod(handle.modified_sym()) { a11y_keyboard_monitor.add_active_virtual_mod(handle.modified_sym()); @@ -1919,15 +2389,15 @@ impl State { "active virtual mods: {:?}", a11y_keyboard_monitor.active_virtual_mods() ); - seat.supressed_keys().add(&handle, None); + seat.supressed_keys().add(backend_id, &handle, None); return FilterResult::Intercept(None); } } // Skip released events for initially surpressed keys - if event.state() == KeyState::Released - && let Some(tokens) = seat.supressed_keys().filter(&handle) + if key_state == KeyState::Released + && let Some(tokens) = seat.supressed_keys().filter(backend_id, &handle) { for token in tokens { self.common.event_loop_handle.remove(token); @@ -1936,7 +2406,7 @@ impl State { } // Handle VT switches - if event.state() == KeyState::Pressed + if key_state == KeyState::Pressed && (Keysym::XF86_Switch_VT_1.raw()..=Keysym::XF86_Switch_VT_12.raw()) .contains(&handle.modified_sym().raw()) { @@ -1945,18 +2415,18 @@ impl State { ) { error!(?err, "Failed switching virtual terminal."); } - seat.supressed_keys().add(&handle, None); + seat.supressed_keys().add(backend_id, &handle, None); return FilterResult::Intercept(None); } if let Some(a11y_keyboard_monitor) = self.common.dbus_state.a11y_keyboard_monitor() - && event.state() == KeyState::Pressed + && key_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(); - seat.supressed_keys().add(&handle, None); + modifiers_queue.clear(backend_id); + seat.supressed_keys().add(backend_id, &handle, None); return FilterResult::Intercept(None); } @@ -1972,11 +2442,11 @@ impl State { // is this a released (triggered) modifier-only binding? if binding.key.is_none() - && event.state() == KeyState::Released + && key_state == KeyState::Released && !cosmic_modifiers_eq_smithay(&binding.modifiers, modifiers) - && modifiers_queue.take(binding) + && modifiers_queue.take(backend_id, binding) { - modifiers_queue.clear(); + modifiers_queue.clear(backend_id); return FilterResult::Intercept(Some(( Action::Shortcut(action.clone()), binding.clone(), @@ -1985,21 +2455,21 @@ impl State { // could this potentially become a modifier-only binding? if binding.key.is_none() - && event.state() == KeyState::Pressed + && key_state == KeyState::Pressed && cosmic_modifiers_eq_smithay(&binding.modifiers, modifiers) { - modifiers_queue.set(binding.clone()); + modifiers_queue.set(backend_id, binding.clone()); clear_queue = false; } // is this a normal binding? if binding.key.is_some() - && event.state() == KeyState::Pressed + && key_state == KeyState::Pressed && key_matches(binding.key.unwrap()) && cosmic_modifiers_eq_smithay(&binding.modifiers, modifiers) { - modifiers_queue.clear(); - seat.supressed_keys().add(&handle, None); + modifiers_queue.clear(backend_id); + seat.supressed_keys().add(backend_id, &handle, None); return FilterResult::Intercept(Some(( Action::Shortcut(action.clone()), binding.clone(), @@ -2010,7 +2480,7 @@ impl State { // no binding if clear_queue { - seat.modifiers_shortcut_queue().clear(); + seat.modifiers_shortcut_queue().clear(backend_id); } // keys are passed through to apps FilterResult::Forward diff --git a/src/lib.rs b/src/lib.rs index d0bf28b7..aa31a464 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -177,11 +177,13 @@ pub fn run(hooks: crate::hooks::Hooks) -> Result<(), Box> { with_xwayland, kiosk_command, ); + // Set up the libei sender side before the backend spawns Xwayland. + let ei_sender = libei::setup_ei(&event_loop.handle()); + state.common.dbus_state.set_ei_sender(ei_sender); + // init backend backend::init_backend_auto(&display, &mut event_loop, &mut state)?; - libei::listen_eis(&event_loop.handle()); - if let Err(err) = theme::watch_theme(event_loop.handle()) { warn!(?err, "Failed to watch theme"); } diff --git a/src/libei.rs b/src/libei.rs index c0c1ed3e..1d748741 100644 --- a/src/libei.rs +++ b/src/libei.rs @@ -1,46 +1,222 @@ -use reis::calloop::EisListenerSource; +use std::os::unix::net::UnixStream; + use reis::eis; use smithay::reexports::reis; -use smithay::backend::libei::{EiInput, EiInputEvent}; -use smithay::input::keyboard::XkbConfig; +use smithay::backend::libei::{EiInput, EiInputEvent, EiRegion}; +use smithay::input::keyboard::Keysym; use smithay::reexports::calloop; +use smithay::wayland::input_method::InputMethodSeat; +use smithay::wayland::text_input::TextInputSeat; +use crate::config::xkb_config_to_wl; +use crate::input::InputBackendId; use crate::state::State; -pub fn listen_eis(handle: &calloop::LoopHandle<'static, State>) { - let listener = match eis::Listener::bind_auto() { - Ok(listener) => listener, - Err(err) => { - tracing::error!("Failed to bind EI listener socket: {}", err); - return; - } - }; +// Requested device types for an EI connection, mirroring the XDG RemoteDesktop portal `DeviceType` bitmask +const DEVICE_TYPE_KEYBOARD: u32 = 1; +const DEVICE_TYPE_POINTER: u32 = 2; +const DEVICE_TYPE_TOUCHSCREEN: u32 = 4; - unsafe { std::env::set_var("LIBEI_SOCKET", listener.path()) }; +// Name of the EI absolute-pointer device. Shared so the connect path and the +// re-advertise-on-output-change path recreate the same device. +const ABSOLUTE_POINTER_NAME: &str = "virtual absolute pointer"; - let listener_source = EisListenerSource::new(listener); +pub type EiRequest = (UnixStream, u32); + +/// Build the regions advertised on the EI absolute devices (absolute pointer and touch) for the +/// output layout: one region per output, each at its **global logical** offset with +/// its logical size and scale. +pub fn absolute_regions(state: &State) -> Vec { + let shell = state.common.shell.read(); + shell + .outputs() + .map(|output| { + let scale = output.current_scale().fractional_scale(); + EiRegion { + // Keep the signed logical rect + // the u32 clamp happens at the `ei_device.region` in smithay. + rect: output.geometry().as_logical(), + scale: scale as f32, + // Tie the region to its output so a client can correlate it with the + // matching screencast stream (the portal advertises the same name). + mapping_id: Some(output.name()), + } + }) + .collect() +} + +/// Re-advertise the coordinate regions on every active EI seat's absolute devices. +pub fn refresh_absolute_pointer_regions(state: &State) { + if state.common.ei_seats.is_empty() { + return; + } + let regions = absolute_regions(state); + for seat in state.common.ei_seats.values() { + seat.update_regions(®ions); + } +} + +pub fn setup_ei( + handle: &calloop::LoopHandle<'static, State>, +) -> calloop::channel::Sender { + let (sender, channel) = calloop::channel::channel::(); let handle_clone = handle.clone(); handle - .insert_source(listener_source, move |context, _, _| { + .insert_source(channel, move |event, _, _| { + let calloop::channel::Event::Msg((stream, device_types)) = event else { + return; + }; + let context = match eis::Context::new(stream) { + Ok(context) => context, + Err(err) => { + tracing::error!("Failed to create EI context: {}", err); + return; + } + }; let source = EiInput::new(context); - handle_clone - .insert_source(source, |event, connection, data| match event { + if let Err(err) = + handle_clone.insert_source(source, move |event, connection, data| match event { EiInputEvent::Connected => { + let conn = connection.eis_connection().clone(); let seat = connection.add_seat("default"); - // TODO config - let _ = seat.add_keyboard("virtual keyboard", XkbConfig::default()); - seat.add_pointer("virtual pointer"); - seat.add_pointer_absolute("virtual absoulte pointer"); - seat.add_touch("virtual touch"); + let wants_keyboard = device_types & DEVICE_TYPE_KEYBOARD != 0; + if wants_keyboard { + let conf = data.common.config.xkb_config(); + // The ei_keyboard device is given the compositor keymap; its key + // events feed the shared seat like any other keyboard. + let _ = seat.add_keyboard("virtual keyboard", xkb_config_to_wl(&conf)); + // The text device lets clients inject keysyms/utf8 directly, delivered + seat.add_text("virtual text"); + } + if device_types & DEVICE_TYPE_POINTER != 0 { + seat.add_pointer("virtual pointer"); + let regions = absolute_regions(data); + seat.add_pointer_absolute(ABSOLUTE_POINTER_NAME, ®ions); + } + if device_types & DEVICE_TYPE_TOUCHSCREEN != 0 { + let regions = absolute_regions(data); + seat.add_touch("virtual touch", ®ions); + } + // Kb-capable connections get a shared-seat source so their `ei_keyboard` + // key events feed the seat keyboard tracking. + if wants_keyboard { + data.common.ei_keyboard_source.insert( + conn.clone(), + smithay::input::keyboard::KeyboardSource::new_auxiliary(), + ); + } + // Track the seat for every connection + data.common.ei_seats.insert(conn, seat); + data.update_ei_input_method(); + } + EiInputEvent::Disconnected => { + let conn = connection.eis_connection().clone(); + let backend_id = InputBackendId::Ei(conn.clone()); + // Release any keys/modifiers and pointer buttons this remote still holds + data.release_ei_keyboard(&conn); + data.release_ei_pointer(&conn); + data.clear_input_source_state(&backend_id); + data.common.ei_seats.remove(&conn); + data.common.ei_keyboard_source.remove(&conn); + data.common.ei_pointer_buttons.remove(&conn); + data.update_ei_input_method(); + // Notify the remaining libei clients of the now-cleared modifier state + let seat = data.common.shell.read().seats.last_active().clone(); + data.broadcast_ei_keyboard_modifiers(&seat); } - EiInputEvent::Disconnected => {} EiInputEvent::Event(event) => { - data.process_input_event(event); + use smithay::backend::input::{InputEvent, KeyboardKeyEvent}; + match event { + InputEvent::Keyboard { event } => { + data.inject_ei_key( + connection.eis_connection(), + event.key_code(), + event.state(), + ); + } + other => { + let backend_id = + InputBackendId::Ei(connection.eis_connection().clone()); + data.process_input_event(other, backend_id); + } + } + } + EiInputEvent::TextKeysym { keysym, state } => { + data.inject_ei_text_keysym(connection.eis_connection(), keysym, state); + } + EiInputEvent::TextUtf8 { text } => { + data.inject_ei_text(&text); } }) - .unwrap(); - Ok(calloop::PostAction::Continue) + { + tracing::error!("Failed to insert EI input source: {}", err); + } }) - .unwrap(); + .expect("Failed to insert EI channel source into the event loop"); + + sender +} + +impl State { + /// Act as the input method for text injection while any text-capable EI connection is + /// active, so `ei_text` UTF-8 can be committed into the focused app even without a real + /// IME, but only when none is bound (a real IME always wins) + pub(crate) fn update_ei_input_method(&mut self) { + let active = !self.common.ei_keyboard_source.is_empty(); + let seats = self + .common + .shell + .read() + .seats + .iter() + .cloned() + .collect::>(); + for seat in seats { + let has_ime = seat.input_method().has_instance(); + let text_input = seat.text_input(); + if active { + if !has_ime { + text_input.set_compositor_input_method(true); + } + } else { + text_input.set_compositor_input_method(false); + } + } + } + + /// Inject UTF-8 text (from an EI `ei_text` device) into the focused client. + pub fn inject_ei_text(&mut self, text: &str) { + let seat = self.common.shell.read().seats.last_active().clone(); + // Only commit through text-input when we're the active input method (no real IME) + if !seat.input_method().has_instance() { + let text_input = seat.text_input(); + let mut injected = false; + text_input.with_active_text_input(|ti, _surface| { + ti.commit_string(Some(text.to_owned())); + injected = true; + }); + if injected { + text_input.done(false); + return; + } + } + + // Bind the whole chunk to spare keycodes in one temporary keymap per batch (delivered + // to just the focused client), so we change the keymap ~once per chunk instead of once + // per character. Leaves the seat's own keyboard state untouched. + let keysyms: Vec = text + .chars() + .map(Keysym::from_char) + .filter(|keysym| keysym.raw() != 0) + .collect(); + let Some(keyboard) = seat.get_keyboard() else { + return; + }; + // At most ~247 keysyms fit one spare keymap (keycodes 9..=255); leave margin. + const BATCH: usize = 240; + for batch in keysyms.chunks(BATCH) { + keyboard.inject_text_keysyms(self, batch); + } + } } diff --git a/src/shell/layout/tiling/grabs/swap.rs b/src/shell/layout/tiling/grabs/swap.rs index 4c22209f..afe93cf0 100644 --- a/src/shell/layout/tiling/grabs/swap.rs +++ b/src/shell/layout/tiling/grabs/swap.rs @@ -13,6 +13,7 @@ use smithay::{ use crate::{ config::key_bindings::cosmic_modifiers_from_smithay, + input::InputBackendId, shell::{Trigger, layout::tiling::NodeDesc}, state::State, }; @@ -73,6 +74,7 @@ impl KeyboardGrab for SwapWindowGrab { data.handle_shortcut_action( shortcuts::Action::Focus(direction), + &InputBackendId::Normal, &self.seat, serial, time, diff --git a/src/shell/seats.rs b/src/shell/seats.rs index 11211e90..27b608cf 100644 --- a/src/shell/seats.rs +++ b/src/shell/seats.rs @@ -5,7 +5,7 @@ use std::{any::Any, cell::RefCell, collections::HashMap, sync::Mutex}; use crate::{ backend::render::cursor::CursorState, config::{Config, xkb_config_to_wl}, - input::{ModifiersShortcutQueue, SupressedButtons, SupressedKeys}, + input::{InputBackendId, ModifiersShortcutQueue, SupressedButtons, SupressedKeys}, state::State, }; use smithay::{ @@ -82,12 +82,25 @@ impl Seats { self.last_active = Some(seat.clone()); } - pub fn for_device(&self, device: &D) -> Option<&Seat> { - self.iter().find(|seat| { - let userdata = seat.user_data(); - let devices = userdata.get::().unwrap(); - devices.has_device(device) - }) + pub fn for_device( + &self, + device: &D, + backend_id: &InputBackendId, + ) -> Option<&Seat> { + self.iter() + .find(|seat| { + let userdata = seat.user_data(); + let devices = userdata.get::().unwrap(); + devices.has_device(device, backend_id) + }) + .or_else(|| { + // EI devices can be transiently unregistered while the compositor recreates + // the absolute-pointer device (e.g. on a scale/geometry change), which would + // otherwise drop all pointer/touch input until the client re-binds it. EI is + // single-seat, so fall back to the active seat here, matching the EI keyboard + // path, which always targets the active seat. + matches!(backend_id, InputBackendId::Ei(_)).then(|| self.last_active()) + }) } } @@ -96,6 +109,7 @@ impl Devices { &self, device: &D, led_state: LedState, + backend_id: &InputBackendId, ) -> Vec { let id = device.id(); let mut map = self.capabilities.borrow_mut(); @@ -113,7 +127,7 @@ impl Devices { .cloned() .filter(|c| map.values().flatten().all(|has| *c != *has)) .collect::>(); - map.insert(id, caps); + map.insert((backend_id.clone(), id), caps); if device.has_capability(DeviceCapability::Keyboard) && let Some(device) = ::downcast_ref::(device) @@ -126,11 +140,18 @@ impl Devices { new_caps } - pub fn has_device(&self, device: &D) -> bool { - self.capabilities.borrow().contains_key(&device.id()) + /// Whether the given backend's device with this id is registered on the seat. + pub fn has_device(&self, device: &D, backend_id: &InputBackendId) -> bool { + self.capabilities + .borrow() + .contains_key(&(backend_id.clone(), device.id())) } - pub fn remove_device(&self, device: &D) -> Vec { + pub fn remove_device( + &self, + device: &D, + backend_id: &InputBackendId, + ) -> Vec { let id = device.id(); let mut keyboards = self.keyboards.borrow_mut(); @@ -139,7 +160,7 @@ impl Devices { } let mut map = self.capabilities.borrow_mut(); - map.remove(&id) + map.remove(&(backend_id.clone(), id)) .unwrap_or_default() .into_iter() .filter(|c| map.values().flatten().all(|has| *c != *has)) @@ -155,7 +176,8 @@ impl Devices { #[derive(Default)] pub struct Devices { - capabilities: RefCell>>, + // Keyed by `(backend, device_id)` + capabilities: RefCell>>, // Used for updating keyboard leds on kms backend keyboards: RefCell>, } @@ -185,7 +207,7 @@ struct FocusedOutput(pub Mutex>); pub struct PointerConstraintHint(pub Mutex)>>); #[derive(Default)] -pub struct LastModifierChange(pub Mutex>); +pub struct LastModifierChange(pub Mutex<(HashMap, Option)>); pub fn create_seat( dh: &DisplayHandle, @@ -265,6 +287,9 @@ pub trait SeatExt { fn supressed_buttons(&self) -> &SupressedButtons; fn modifiers_shortcut_queue(&self) -> &ModifiersShortcutQueue; fn last_modifier_change(&self) -> Option; + fn last_modifier_change_for(&self, backend_id: &InputBackendId) -> Option; + fn set_last_modifier_change(&self, backend_id: &InputBackendId, serial: Serial); + fn clear_last_modifier_change(&self, backend_id: &InputBackendId); fn pointer_constraint_hint(&self) -> Option<(WlSurface, Point)>; fn set_pointer_constraint_hint(&self, hint: Option<(WlSurface, Point)>); @@ -343,13 +368,48 @@ impl SeatExt for Seat { } fn last_modifier_change(&self) -> Option { - *self - .user_data() + self.user_data() .get::() .unwrap() .0 .lock() .unwrap() + .1 + } + + fn last_modifier_change_for(&self, backend_id: &InputBackendId) -> Option { + self.user_data() + .get::() + .unwrap() + .0 + .lock() + .unwrap() + .0 + .get(backend_id) + .copied() + } + + fn set_last_modifier_change(&self, backend_id: &InputBackendId, serial: Serial) { + let mut guard = self + .user_data() + .get::() + .unwrap() + .0 + .lock() + .unwrap(); + guard.0.insert(backend_id.clone(), serial); + guard.1 = Some(serial); + } + + fn clear_last_modifier_change(&self, backend_id: &InputBackendId) { + self.user_data() + .get::() + .unwrap() + .0 + .lock() + .unwrap() + .0 + .remove(backend_id); } fn pointer_constraint_hint(&self) -> Option<(WlSurface, Point)> { diff --git a/src/state.rs b/src/state.rs index 8cfee1a9..5ccc7a7b 100644 --- a/src/state.rs +++ b/src/state.rs @@ -248,6 +248,28 @@ pub struct Common { pub gesture_state: Option, + /// Active libei sender seats, keyed by their `eis` connection. Tracked so their virtual + /// keyboards can be re-created when the keyboard configuration changes at runtime. + pub ei_seats: std::collections::HashMap< + smithay::reexports::reis::eis::Connection, + smithay::backend::libei::EiInputSeat, + >, + + /// The shared-seat [`KeyboardSource`] assigned to each libei connection, so its + /// `ei_keyboard` key events feed the seat keyboard with independent per-source hold + /// tracking (and can be released together on disconnect). Keyed by connection. + pub ei_keyboard_source: std::collections::HashMap< + smithay::reexports::reis::eis::Connection, + smithay::input::keyboard::KeyboardSource, + >, + + /// Pointer buttons currently held by each libei connection, so they can be released when the + /// connection drops + pub ei_pointer_buttons: std::collections::HashMap< + smithay::reexports::reis::eis::Connection, + std::collections::HashSet, + >, + pub kiosk_child: Option, pub theme: cosmic::Theme, @@ -757,6 +779,9 @@ impl State { should_stop: false, kiosk_exit_code: None, gesture_state: None, + ei_seats: std::collections::HashMap::new(), + ei_keyboard_source: std::collections::HashMap::new(), + ei_pointer_buttons: std::collections::HashMap::new(), kiosk_child: None, theme: cosmic::theme::system_preference(), diff --git a/src/wayland/handlers/output_configuration.rs b/src/wayland/handlers/output_configuration.rs index 6ab678f5..4f7000fd 100644 --- a/src/wayland/handlers/output_configuration.rs +++ b/src/wayland/handlers/output_configuration.rs @@ -230,6 +230,16 @@ impl State { state.common.output_configuration_state.update(); }); + // Output scale or geometry may have changed. EI absolute-pointer regions + // are immutable per device, so any connected EI client (e.g. an RDP server) + // keeps mapping with the old scale until it reconnects. Recreate the + // device with the updated region so the mapping tracks the change live. + // (drop the backend lock first: refresh borrows `self` immutably.) + drop(backend); + if !test_only { + crate::libei::refresh_absolute_pointer_regions(self); + } + true } } diff --git a/src/xwayland.rs b/src/xwayland.rs index 83978b6e..da3fb36b 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -115,7 +115,7 @@ impl State { &self.common.display_handle, None, std::iter::empty::<(OsString, OsString)>(), - std::iter::empty::(), + ["-enable-ei-portal"], true, Stdio::null(), Stdio::null(), @@ -394,6 +394,7 @@ impl Common { sym: Keysym, code: Keycode, state: KeyState, + modifiers: ModifiersState, serial: Serial, time: u32, ) { @@ -418,7 +419,6 @@ impl Common { .last_active() .get_keyboard() .unwrap(); - let modifiers = keyboard.modifier_state(); let is_modifier = sym.is_modifier_key(); let xstate = self.xwayland_state.as_mut().unwrap(); From c423e8282029bd2769d5a93a738b3e2092d5095a Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Fri, 19 Jun 2026 18:38:26 -0700 Subject: [PATCH 21/80] libei: on KMS, schedule render after handling input events Matches behavior of libinput backend. --- src/libei.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libei.rs b/src/libei.rs index 1d748741..ad3a674d 100644 --- a/src/libei.rs +++ b/src/libei.rs @@ -11,7 +11,8 @@ use smithay::wayland::text_input::TextInputSeat; use crate::config::xkb_config_to_wl; use crate::input::InputBackendId; -use crate::state::State; +use crate::state::{BackendData, State}; +use crate::utils::{geometry::RectGlobalExt, prelude::OutputExt}; // Requested device types for an EI connection, mirroring the XDG RemoteDesktop portal `DeviceType` bitmask const DEVICE_TYPE_KEYBOARD: u32 = 1; @@ -139,6 +140,11 @@ pub fn setup_ei( let backend_id = InputBackendId::Ei(connection.eis_connection().clone()); data.process_input_event(other, backend_id); + if matches!(data.backend, BackendData::Kms(_)) { + for output in data.common.shell.read().outputs() { + data.backend.kms().schedule_render(output); + } + } } } } From 861a460e6c66b26bc910e6a96afd96b8e45d9861 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Wed, 5 Aug 2026 01:47:07 -0700 Subject: [PATCH 22/80] chore: Fix `clippy` lints --- src/input/mod.rs | 2 +- src/wayland/handlers/xdg_shell/mod.rs | 20 +++++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index c93b3405..67780d53 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -3054,7 +3054,7 @@ pub fn update_output_image_copy_cursor_position( position: Point, ) { let output_geometry = output.geometry(); - for session in cursor_sessions_for_output(&shell, &output) { + for session in cursor_sessions_for_output(shell, output) { if let Some(cursor_geometry) = seat.cursor_geometry( (position - output_geometry.loc.to_f64()) .as_logical() diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index b8288019..b4f16c4c 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -333,17 +333,15 @@ impl XdgShellHandler for State { if let Some(ref grab) = grab { if grab.has_ended() { should_ungrab = true; - } else if let Some(target) = grab.current_grab() { - if let Some(wl_surface) = target.wl_surface() { - if wl_surface.as_ref() == surface.wl_surface() - || smithay::desktop::PopupManager::popups_for_surface( - surface.wl_surface(), - ) - .any(|(p, _)| p.wl_surface() == wl_surface.as_ref()) - { - should_ungrab = true; - } - } + } else if let Some(target) = grab.current_grab() + && let Some(wl_surface) = target.wl_surface() + && (wl_surface.as_ref() == surface.wl_surface() + || smithay::desktop::PopupManager::popups_for_surface( + surface.wl_surface(), + ) + .any(|(p, _)| p.wl_surface() == wl_surface.as_ref())) + { + should_ungrab = true; } } if should_ungrab { From 8806436f81a82c38e7d18e1ec2ff1edc201faacc Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 17 Aug 2026 09:35:31 -0700 Subject: [PATCH 23/80] libei: Add `com.system76.CosmicOSK` to DBus whitelist --- src/dbus/ei.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/dbus/ei.rs b/src/dbus/ei.rs index 6391dc76..263bced4 100644 --- a/src/dbus/ei.rs +++ b/src/dbus/ei.rs @@ -8,9 +8,10 @@ use zbus::names::{UniqueName, WellKnownName}; use super::name_owners::NameOwners; -static ALLOWED_NAMES: &[WellKnownName] = &[WellKnownName::from_static_str_unchecked( - "org.freedesktop.impl.portal.desktop.cosmic", -)]; +static ALLOWED_NAMES: &[WellKnownName] = &[ + WellKnownName::from_static_str_unchecked("org.freedesktop.impl.portal.desktop.cosmic"), + WellKnownName::from_static_str_unchecked("com.system76.CosmicOSK"), +]; /// Channel for handing the EI socketpair (and requested device types) /// It's `None` until the EI sender side has been set up From f1d1c0025769b48507b3991a2f7dc0ccbba7ec94 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Tue, 18 Aug 2026 11:09:23 +0200 Subject: [PATCH 24/80] i18n: translation updates from weblate Co-authored-by: Michael Millet Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-comp/da/ Translation: Pop OS/COSMIC Comp --- resources/i18n/da/cosmic_comp.ftl | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/i18n/da/cosmic_comp.ftl b/resources/i18n/da/cosmic_comp.ftl index bd0e1edb..ad4befea 100644 --- a/resources/i18n/da/cosmic_comp.ftl +++ b/resources/i18n/da/cosmic_comp.ftl @@ -25,3 +25,4 @@ window-menu-resize-edge-bottom = Bund window-menu-minimize = Minimér window-menu-maximize = Maksimér window-menu-fullscreen = Fuld skærm +unknown-keybinding = From 04026259e0707e6c2f2bb0f26e113e48c5cb19fd Mon Sep 17 00:00:00 2001 From: chris-010 <10660568+chris-010@users.noreply.github.com> Date: Sat, 15 Aug 2026 17:36:41 +0200 Subject: [PATCH 25/80] fix: treat unlocking the session as user activity SessionLockHandler::unlock clears the lock and schedules a render, but never tells IdleNotifierState that the user is there. Unlock paths that produce no input event -- fingerprint and smartcard readers -- therefore leave the idle clock running across the entire lock period, and it is already past its timeouts by the time the session is unlocked. The visible result is that the screen stays off after a successful unlock until the user moves the mouse or presses a key, or that the session locks itself again seconds after being unlocked. Notifying activity for every seat in unlock() resets the timers the same way an input event would. Drafted with AI assistance (Claude Code); the change was reviewed, built and tested by me on real hardware as described. Signed-off-by: chris-010 <10660568+chris-010@users.noreply.github.com> --- src/wayland/handlers/session_lock.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/wayland/handlers/session_lock.rs b/src/wayland/handlers/session_lock.rs index 39e1e375..c4e93ec5 100644 --- a/src/wayland/handlers/session_lock.rs +++ b/src/wayland/handlers/session_lock.rs @@ -46,6 +46,11 @@ impl SessionLockHandler for State { let mut shell = self.common.shell.write(); shell.session_lock = None; + let seats = shell.seats.iter().cloned().collect::>(); + for seat in &seats { + self.common.idle_notifier_state.notify_activity(seat); + } + for output in shell.outputs() { self.backend.schedule_render(output); } From f20de50e4124c14d55aa72ca105b59e743edae8d Mon Sep 17 00:00:00 2001 From: Hojjat Date: Mon, 20 Jul 2026 16:11:13 -0600 Subject: [PATCH 26/80] feat: shake cursor to find by magnifying the cursor --- cosmic-comp-config/src/lib.rs | 3 + src/backend/render/cursor.rs | 158 +++++++++++++++++++++++++++++++++- src/backend/render/element.rs | 4 +- src/backend/render/mod.rs | 23 +++-- src/config/mod.rs | 4 + src/input/mod.rs | 15 ++++ src/shell/mod.rs | 5 ++ 7 files changed, 205 insertions(+), 7 deletions(-) diff --git a/cosmic-comp-config/src/lib.rs b/cosmic-comp-config/src/lib.rs index e5e73883..864d6737 100644 --- a/cosmic-comp-config/src/lib.rs +++ b/cosmic-comp-config/src/lib.rs @@ -101,6 +101,8 @@ pub struct CosmicCompConfig { pub appearance_settings: AppearanceConfig, /// Hide the cursor after this many seconds of pointer inactivity (None disables) pub cursor_hide_timeout: Option, + /// Briefly magnify the cursor when the pointer is shaken, to help locate it + pub cursor_shake_to_find: bool, pub activation_policy: ActivationPolicy, } @@ -139,6 +141,7 @@ impl Default for CosmicCompConfig { accessibility_zoom: ZoomConfig::default(), appearance_settings: AppearanceConfig::default(), cursor_hide_timeout: None, + cursor_shake_to_find: true, activation_policy: ActivationPolicy::default(), } } diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index da01b270..30243dad 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -8,6 +8,7 @@ use crate::{ utils::prelude::*, wayland::handlers::compositor::FRAME_TIME_FILTER, }; +use keyframe::{ease, functions::EaseInOutCubic}; use smithay::{ backend::{ allocator::Fourcc, @@ -38,7 +39,7 @@ use smithay::{ wayland::compositor::{get_role, with_states}, }; use std::{ - collections::HashMap, + collections::{HashMap, VecDeque}, io::Read, sync::Mutex, time::{Duration, Instant}, @@ -270,6 +271,43 @@ pub struct CursorStateInner { hidden: bool, idle_timer: Option, last_armed: Option, + + // shake-to-find + shake_path: VecDeque, + shake_path_position: Point, + magnify_until: Option, + magnify_target: f32, + magnification: f32, + anim_from: f32, + anim_start: Option, +} + +/// One sampled pointer position on the recent motion path. +#[derive(Clone, Copy)] +struct PathSample { + position: Point, + time: Instant, +} + +/// How far back the motion path is considered when looking for a shake. +const SHAKE_INTERVAL: Duration = Duration::from_millis(1000); +/// Path-length / bounding-box-diagonal ratio required to count as a shake. +const SHAKE_SENSITIVITY: f64 = 4.0; +/// Minimum bounding-box diagonal (logical px) before a shake is considered. +const SHAKE_DIAGONAL_MIN: f64 = 100.0; +/// Two deltas count as "the same direction" if both lie within this tolerance. +const SHAKE_SAME_SIGN_TOLERANCE: f64 = 1.0; +/// Keep the cursor enlarged for this long after the last detected shake. +const SHAKE_HOLD: Duration = Duration::from_millis(2000); +/// Extra magnification added by each shake, growing from the normal cursor size. +const OVER_MAGNIFICATION: f32 = 1.0; +/// Duration of the grow/shrink animation. +const MAGNIFICATION_ANIM: Duration = Duration::from_millis(200); + +/// small movement is ignored and direction stays the same +fn same_direction(a: f64, b: f64) -> bool { + (a >= -SHAKE_SAME_SIGN_TOLERANCE && b >= -SHAKE_SAME_SIGN_TOLERANCE) + || (a <= SHAKE_SAME_SIGN_TOLERANCE && b <= SHAKE_SAME_SIGN_TOLERANCE) } impl CursorStateInner { @@ -290,6 +328,116 @@ impl CursorStateInner { pub fn size(&self) -> u32 { self.cursor_size } + + /// Feed one relative-motion event into the shake detector. + pub fn detect_shake(&mut self, delta: Point, now: Instant) { + // Drop samples that have aged out of the time window. + while let Some(oldest) = self.shake_path.front() { + if now.duration_since(oldest.time) >= SHAKE_INTERVAL { + self.shake_path.pop_front(); + } else { + break; + } + } + + if delta.x != 0.0 || delta.y != 0.0 { + self.shake_path_position += delta; + let sample = PathSample { + position: self.shake_path_position, + time: now, + }; + + if self.shake_path.len() >= 2 { + let last = self.shake_path[self.shake_path.len() - 1].position; + let prev = self.shake_path[self.shake_path.len() - 2].position; + let last_delta = last - prev; + if same_direction(last_delta.x, delta.x) && same_direction(last_delta.y, delta.y) { + *self.shake_path.back_mut().unwrap() = sample; + } else { + self.shake_path.push_back(sample); + } + } else { + self.shake_path.push_back(sample); + } + } + + if self.shake_path.len() < 2 { + return; + } + + let first = self.shake_path[0].position; + let (mut left, mut top, mut right, mut bottom) = (first.x, first.y, first.x, first.y); + let mut path_length = 0.0; + for i in 1..self.shake_path.len() { + let p = self.shake_path[i].position; + left = left.min(p.x); + top = top.min(p.y); + right = right.max(p.x); + bottom = bottom.max(p.y); + + let step = p - self.shake_path[i - 1].position; + path_length += step.x.hypot(step.y); + } + + let diagonal = (right - left).hypot(bottom - top); + if diagonal < SHAKE_DIAGONAL_MIN { + return; + } + + // Path noticeably longer than the diagonal => a shake gesture. + if path_length / diagonal > SHAKE_SENSITIVITY { + self.grow(now); + self.shake_path.clear(); + } + } + + /// grow the cursor by one more increment (unbounded) + fn grow(&mut self, now: Instant) { + self.animate_to(self.magnify_target + OVER_MAGNIFICATION, now); + self.magnify_until = Some(now + SHAKE_HOLD); + } + + /// Start a 200ms `InOutCubic` tween from the current size to `target`. + fn animate_to(&mut self, target: f32, now: Instant) { + if (target - self.magnify_target).abs() < f32::EPSILON { + return; + } + self.anim_from = self.magnification; + self.anim_start = Some(now); + self.magnify_target = target; + } + + /// Advance the magnification animation and return the current factor. + pub fn animated_magnification(&mut self, now: Instant) -> f32 { + // Begin shrinking back once the hold window elapses. + if let Some(until) = self.magnify_until + && now >= until + { + self.magnify_until = None; + self.animate_to(1.0, now); + } + + self.magnification = match self.anim_start { + Some(start) => { + // `ease` clamps the time to `0.0..=1.0` for us. + let t = now.duration_since(start).as_secs_f32() / MAGNIFICATION_ANIM.as_secs_f32(); + if t >= 1.0 { + self.anim_start = None; + } + ease(EaseInOutCubic, self.anim_from, self.magnify_target, t) + } + None => self.magnify_target, + }; + self.magnification + } + + /// Whether the cursor is currently magnified or pending; drives continued redraws. + pub fn is_magnifying(&self) -> bool { + self.magnify_until.is_some() + || self.anim_start.is_some() + || self.magnification > 1.001 + || self.magnify_target > 1.001 + } } pub fn load_cursor_env() -> (String, u32) { @@ -324,6 +472,14 @@ impl Default for CursorStateInner { hidden: false, idle_timer: None, last_armed: None, + + shake_path: VecDeque::new(), + shake_path_position: Point::from((0.0, 0.0)), + magnify_until: None, + magnify_target: 1.0, + magnification: 1.0, + anim_from: 1.0, + anim_start: None, } } } diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index e1259dc1..cd2fb5c2 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -41,7 +41,9 @@ where Workspace( RelocateRenderElement>>>, ), - Cursor(RescaleRenderElement>>), + Cursor( + RescaleRenderElement>>>, + ), Dnd(SurfaceRenderElement), MoveGrab(RescaleRenderElement>), Postprocess( diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index e9cf651f..b348640f 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -510,22 +510,35 @@ pub fn cursor_elements<'a, 'frame, R>( }; let location = pointer.current_location() - output.current_location().to_f64(); + // Shake-to-find magnification, applied around the pointer tip. + let cursor_magnification = seat + .user_data() + .get::() + .map_or(1.0, |s| { + s.lock().unwrap().animated_magnification(Instant::now()) + }); + let cursor_center = location.to_physical(scale).to_i32_round(); + if mode != CursorMode::None { cursor::draw_cursor( renderer, seat, location, scale.into(), - zoom_scale, + zoom_scale * cursor_magnification as f64, now, blur_strength, mode != CursorMode::NotDefault, &mut |elem, hotspot| { push(CosmicElement::Cursor(RescaleRenderElement::from_element( - RelocateRenderElement::from_element( - elem, - Point::from((-hotspot.x, -hotspot.y)), - Relocate::Relative, + RescaleRenderElement::from_element( + RelocateRenderElement::from_element( + elem, + Point::from((-hotspot.x, -hotspot.y)), + Relocate::Relative, + ), + cursor_center, + cursor_magnification as f64, ), focal_point .as_logical() diff --git a/src/config/mod.rs b/src/config/mod.rs index 4a2500fd..93b53374 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -969,6 +969,10 @@ fn config_changed(config: cosmic_config::Config, keys: Vec, state: &mut } } } + "cursor_shake_to_find" => { + let new = get_config::(&config, "cursor_shake_to_find"); + state.common.config.cosmic_conf.cursor_shake_to_find = new; + } "cursor_hide_timeout" => { let new = get_config::>(&config, "cursor_hide_timeout"); if new != state.common.config.cosmic_conf.cursor_hide_timeout { diff --git a/src/input/mod.rs b/src/input/mod.rs index 67780d53..0e307079 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -337,6 +337,21 @@ impl State { notify_cursor_activity(self, &seat); let current_output = seat.active_output(); + if self.common.config.cosmic_conf.cursor_shake_to_find + && let Some(cursor_state) = + seat.user_data() + .get::() + { + let active = { + let mut cursor = cursor_state.lock().unwrap(); + cursor.detect_shake(event.delta(), std::time::Instant::now()); + cursor.is_magnifying() + }; + if active { + self.backend.schedule_render(¤t_output); + } + } + let mut position = seat.get_pointer().unwrap().current_location().as_global(); let under = State::surface_under(position, ¤t_output, &shell) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index d9f95976..0baa3713 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -2286,6 +2286,11 @@ impl Shell { .is_some_and(|state| state.lock().unwrap().is_animating()) }) }) + || self.seats.iter().any(|seat| { + seat.user_data() + .get::() + .is_some_and(|state| state.lock().unwrap().is_magnifying()) + }) } pub fn update_animations(&mut self) -> HashMap { From be1fab51eae804a2f620b5404b231a2090c39031 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Fri, 21 Aug 2026 09:54:17 +0200 Subject: [PATCH 27/80] comp: retire le runtime Tokio, devenu sans objet Revient sur 3d01a219. La feature "tokio" de libcosmic-yoda activait zbus?/tokio, ce qui imposait un Handle::current() ambiant alors que la boucle de cosmic-comp n'est pas async ; d'ou le garde de runtime dans main(). La feature est retiree de la dependance libcosmic, zbus n'a donc plus besoin de reacteur Tokio et le garde n'a plus lieu d'etre. Cargo.lock perd tokio et toute la chaine qui venait avec la feature : ashpd, rfd, atspi, accesskit_unix, zbus_xml, tokio-stream. Co-Authored-By: Claude Opus 5 (1M context) --- Cargo.lock | 326 ++++++++-------------------------------------------- Cargo.toml | 2 - src/main.rs | 9 -- 3 files changed, 50 insertions(+), 287 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f4bc60d..c4db0004 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,18 +26,6 @@ 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" @@ -60,21 +48,6 @@ dependencies = [ "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" @@ -95,7 +68,6 @@ source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111 dependencies = [ "accesskit", "accesskit_macos", - "accesskit_unix", "accesskit_windows", "raw-window-handle", "winit 0.31.0-beta.2", @@ -264,24 +236,6 @@ 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" @@ -294,7 +248,6 @@ dependencies = [ "rand 0.9.2", "serde", "serde_repr", - "tokio", "url", "wayland-backend", "wayland-client", @@ -395,7 +348,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -430,7 +383,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -449,43 +402,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "atspi" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -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.8" @@ -495,7 +411,7 @@ dependencies = [ "derive_utils", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -681,7 +597,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1032,7 +948,6 @@ dependencies = [ "smithay-egui", "thiserror 2.0.18", "tiny-skia 0.11.4", - "tokio", "tracing", "tracing-journald", "tracing-subscriber", @@ -1074,7 +989,6 @@ dependencies = [ "notify", "ron 0.12.0", "serde", - "tokio", "tracing", "xdg", "zbus", @@ -1085,7 +999,7 @@ name = "cosmic-config-derive" version = "1.0.0" dependencies = [ "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1302,7 +1216,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.117", + "syn", ] [[package]] @@ -1313,7 +1227,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1341,7 +1255,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1352,7 +1266,7 @@ checksum = "ccfae181bab5ab6c5478b2ccb69e4c68a02f8c3ec72f6616bfec9dbc599d2ee0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1394,8 +1308,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ "bitflags 2.13.1", - "block2 0.6.2", - "libc", "objc2 0.6.4", ] @@ -1407,7 +1319,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1610,7 +1522,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1631,7 +1543,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1908,7 +1820,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -2004,7 +1916,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -2406,7 +2318,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.117", + "syn", "unic-langid", ] @@ -2420,7 +2332,7 @@ dependencies = [ "i18n-config", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -2518,7 +2430,6 @@ dependencies = [ "iced_core", "log", "rustc-hash 2.1.1", - "tokio", "wasm-bindgen-futures", "wasmtimer", ] @@ -2951,7 +2862,7 @@ checksum = "470252db18ecc35fd766c0891b1e3ec6cbbcd62507e85276c01bf75d8e94d4a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -3150,7 +3061,7 @@ name = "libcosmic-yoda" version = "0.1.0-yoda.2" dependencies = [ "apply", - "ashpd 0.12.3", + "ashpd", "auto_enums", "build_helpers", "cosmic-client-toolkit", @@ -3179,13 +3090,11 @@ dependencies = [ "log", "palette", "phf 0.13.1", - "rfd", "rust-embed", "serde", "slotmap", "taffy", "thiserror 2.0.18", - "tokio", "tracing", "unicode-segmentation", "url", @@ -3213,7 +3122,7 @@ checksum = "8dc2c710cf5819e91220a446d9e64acc6814386cc22c509c3f0df83c0b874a98" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -3817,7 +3726,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -3888,7 +3797,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ "bitflags 2.13.1", - "block2 0.6.2", "objc2 0.6.4", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -4099,7 +4007,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -4133,7 +4041,7 @@ dependencies = [ "by_address", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -4228,7 +4136,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.117", + "syn", "unicase", ] @@ -4242,7 +4150,7 @@ dependencies = [ "phf_shared 0.13.1", "proc-macro2", "quote", - "syn 2.0.117", + "syn", "uncased", ] @@ -4289,7 +4197,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -4385,12 +4293,6 @@ 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" @@ -4443,7 +4345,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.117", + "syn", ] [[package]] @@ -4474,7 +4376,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -4494,7 +4396,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "version_check", "yansi", ] @@ -4516,7 +4418,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" dependencies = [ "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -4541,7 +4443,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" dependencies = [ "memchr", - "serde", ] [[package]] @@ -4736,7 +4637,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -4817,30 +4718,6 @@ 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" @@ -4909,7 +4786,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.117", + "syn", "walkdir", ] @@ -5097,7 +4974,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -5122,7 +4999,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -5162,7 +5039,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -5397,16 +5274,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27207bb65232eda1f588cf46db2fee75c0808d557f6b3cf19a75f5d6d7c94df1" -[[package]] -name = "socket2" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - [[package]] name = "softbuffer" version = "0.4.1" @@ -5525,17 +5392,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "syn" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "synstructure" version = "0.13.2" @@ -5544,7 +5400,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -5635,7 +5491,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -5646,7 +5502,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -5779,45 +5635,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "tokio" -version = "1.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" -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.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.3", -] - -[[package]] -name = "tokio-stream" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - [[package]] name = "toml" version = "0.5.11" @@ -5898,7 +5715,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -6144,12 +5961,6 @@ dependencies = [ "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" @@ -6336,7 +6147,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn", "wasm-bindgen-shared", ] @@ -6877,7 +6688,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -6888,7 +6699,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -7735,7 +7546,7 @@ dependencies = [ "heck 0.5.0", "indexmap 2.13.0", "prettyplease", - "syn 2.0.117", + "syn", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -7751,7 +7562,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.117", + "syn", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -7952,7 +7763,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "synstructure", ] @@ -7981,7 +7792,6 @@ dependencies = [ "rustix 1.1.4", "serde", "serde_repr", - "tokio", "tracing", "uds_windows", "uuid", @@ -7992,30 +7802,6 @@ dependencies = [ "zvariant", ] -[[package]] -name = "zbus-lockstep" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -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 2.0.117", - "zbus-lockstep", - "zbus_xml", - "zvariant", -] - [[package]] name = "zbus_macros" version = "5.16.0" @@ -8025,7 +7811,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn", "zbus_names", "zvariant", "zvariant_utils", @@ -8042,18 +7828,6 @@ dependencies = [ "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", -] - [[package]] name = "zeno" version = "0.3.3" @@ -8077,7 +7851,7 @@ checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -8097,7 +7871,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "synstructure", ] @@ -8132,7 +7906,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -8195,7 +7969,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn", "zvariant_utils", ] @@ -8208,7 +7982,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.117", + "syn", "winnow 1.0.3", ] diff --git a/Cargo.toml b/Cargo.toml index d754b61e..86bb5159 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,6 @@ iced_graphics = { path = "../libcosmic/iced/graphics", features = ["geometry"] } indexmap = "2.13" keyframe = "1.1.1" cosmic = { package = "libcosmic-yoda", path = "../libcosmic", default-features = false, features = [ - "tokio", "wayland", "multi-window", ] } @@ -78,7 +77,6 @@ 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 ffa37236..7ae05991 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,6 @@ // 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 8ac198de1559c4a396dbc00336637bc6af138243 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Fri, 21 Aug 2026 09:54:25 +0200 Subject: [PATCH 28/80] kms: revient au mode prefere pour le choix initial de sortie Revient sur 7187efd5, qui elisait le rafraichissement le plus eleve a la resolution native au lieu du mode marque PREFERRED. Sans effet au quotidien : ~/.local/state/cosmic-comp/outputs.ron memorise le mode choisi par sortie et pin deja DP-1 en 3840x2160 a 119,88 Hz. populate_modes ne decide qu'en l'absence d'etat memorise, c'est-a-dire sur un profil neuf ou un ecran jamais vu. Co-Authored-By: Claude Opus 5 (1M context) --- src/backend/kms/device.rs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/src/backend/kms/device.rs b/src/backend/kms/device.rs index 7df3c8f9..418a16f3 100644 --- a/src/backend/kms/device.rs +++ b/src/backend/kms/device.rs @@ -1241,26 +1241,15 @@ fn populate_modes( ) -> Result<()> { let conn_info = drm.get_connector(conn, false)?; let max_bpc = drm_helpers::get_max_bpc(drm, conn)?.map(|(_val, range)| range.end.min(16)); - let preferred = conn_info + let Some(mode) = conn_info .modes() .iter() .find(|mode| mode.mode_type().contains(ModeTypeFlags::PREFERRED)) .copied() - .or(conn_info.modes().first().copied()); - let Some(preferred) = preferred else { + .or(conn_info.modes().first().copied()) + else { anyhow::bail!("No mode found"); }; - // Prefer the highest refresh rate at the native resolution so that - // high-refresh panels (120 Hz, 144 Hz, …) are used out-of-the-box - // without a pre-existing outputs.ron config. - let preferred_size = preferred.size(); - let mode = conn_info - .modes() - .iter() - .filter(|m| m.size() == preferred_size) - .max_by_key(|m| drm_helpers::calculate_refresh_rate(**m)) - .copied() - .unwrap_or(preferred); let refresh_rate = drm_helpers::calculate_refresh_rate(mode); let output_mode = OutputMode { From 738e0b312497dec066a611dc5ea98b75df39058e Mon Sep 17 00:00:00 2001 From: Tom Grushka Date: Wed, 22 Jul 2026 00:50:14 -0600 Subject: [PATCH 29/80] a11y: magnifier: change zoom stepping to multiplicative, bound max, add presets 1. Change magnifier zoom stepping from additive to multiplicative, because zoom is a ratio, not a difference, and human perception is logarithmic (browser zoom steps, map zoom levels, microscope powers). 2. Add `pub const ZoomConfig::ZOOM_INCREMENT_PRESETS` with two additional presets: 10% (macOS, Cinnamon) and 75% (for users at 70-90x zoom). 3. Bound the zoom level to a generous maximum, beyond what anyone is expected to need, to prevent runaway / unrecoverable levels (#1230). Full background and rationale at #2615. I used AI to assist with research and issue writeup. Code is 100% mine. - Closes #2615. - Fixes #1239. - Partially addresses #1230 and #1237. --- cosmic-comp-config/src/lib.rs | 4 ++++ src/input/actions.rs | 10 +++++++++- src/shell/zoom.rs | 6 +++--- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/cosmic-comp-config/src/lib.rs b/cosmic-comp-config/src/lib.rs index 864d6737..853ba447 100644 --- a/cosmic-comp-config/src/lib.rs +++ b/cosmic-comp-config/src/lib.rs @@ -198,6 +198,10 @@ pub struct ZoomConfig { pub enable_mouse_zoom_shortcuts: bool, } +impl ZoomConfig { + pub const ZOOM_INCREMENT_PRESETS: &[u32] = &[10, 25, 50, 75, 100, 150, 200]; +} + impl Default for ZoomConfig { fn default() -> Self { ZoomConfig { diff --git a/src/input/actions.rs b/src/input/actions.rs index a4932597..adabb8a6 100644 --- a/src/input/actions.rs +++ b/src/input/actions.rs @@ -29,6 +29,8 @@ use std::{os::unix::process::CommandExt, thread}; use super::gestures; +const MAX_ZOOM: f64 = 256.0; + fn propagate_by_default(action: &shortcuts::Action) -> bool { matches!( action, @@ -1116,7 +1118,13 @@ impl State { } if zoom_seat == *seat { - let new_level = (current_level + change).max(1.0); + let factor = 1.0 + change.abs(); + let new_level = if change < 0. { + current_level / factor + } else { + current_level * factor + } + .clamp(1.0, MAX_ZOOM); shell.trigger_zoom( seat, Some(&output), diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index ef81053f..0c0e9340 100644 --- a/src/shell/zoom.rs +++ b/src/shell/zoom.rs @@ -7,7 +7,7 @@ use cosmic::{ theme, widget::{self, icon::Named}, }; -use cosmic_comp_config::ZoomMovement; +use cosmic_comp_config::{ZoomConfig, ZoomMovement}; use cosmic_config::ConfigSet; use keyframe::{ease, functions::Linear}; use smithay::{ @@ -447,7 +447,7 @@ pub enum MenuMessage { impl ZoomProgram { pub fn new(level: f64, movement: ZoomMovement, increment: u32) -> Self { - let mut increments = vec![25, 50, 100, 150, 200]; + let mut increments = ZoomConfig::ZOOM_INCREMENT_PRESETS.to_vec(); if !increments.contains(&increment) { increments.push(increment); } @@ -828,7 +828,7 @@ impl Program for ZoomProgram { if let Some(pos) = self.increments.iter().position(|val| *val == increment) { self.increment_idx = pos; } else { - let mut increments = vec![25, 50, 100, 150, 200]; + let mut increments = ZoomConfig::ZOOM_INCREMENT_PRESETS.to_vec(); if !increments.contains(&increment) { increments.push(increment); } From 8304b18886cd94c6ecf16bf0215c78f8c2d7e5cc Mon Sep 17 00:00:00 2001 From: Tom Grushka <89836285+tgrushka@users.noreply.github.com> Date: Tue, 28 Jul 2026 14:47:53 -0600 Subject: [PATCH 30/80] Clamp zoom level to 1.0 if < 1.01 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix floating point edge cases that keep zoom enabled when level ≈ 1.0 Co-authored-by: Luuk van der Duim --- src/input/actions.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/input/actions.rs b/src/input/actions.rs index adabb8a6..8a5ef84c 100644 --- a/src/input/actions.rs +++ b/src/input/actions.rs @@ -1125,6 +1125,7 @@ impl State { current_level * factor } .clamp(1.0, MAX_ZOOM); + let new_level = if new_level < 1.01 { 1.0 } else { new_level }; shell.trigger_zoom( seat, Some(&output), From 01b79c3eae39c080b525b15d8ba3088ada9e8591 Mon Sep 17 00:00:00 2001 From: Hojjat Date: Fri, 31 Jul 2026 14:46:52 -0600 Subject: [PATCH 31/80] feat: scalable cursor support --- Cargo.lock | 5 +- Cargo.toml | 1 + src/backend/render/cursor.rs | 301 ++++++++++++++++++++++++++++++----- 3 files changed, 262 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f2364e9e..b8be66ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -817,6 +817,7 @@ dependencies = [ "profiling", "rand 0.10.0", "regex", + "resvg 0.45.1", "ron 0.12.0", "rust-embed", "rustix 1.1.4", @@ -6893,9 +6894,9 @@ checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" [[package]] name = "xcursor" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" +checksum = "163b33ed8786455e2fa5d72f554057ce3f3182425434f756cd39c99839d88e23" [[package]] name = "xdg" diff --git a/Cargo.toml b/Cargo.toml index 7d4ff3af..d807d369 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ log-panics = { version = "2", features = ["with-backtrace"] } ordered-float = "5.1" png = "0.18" regex = "1" +resvg = "0.45" ron = "0.12" rust-embed = { version = "8.11", features = ["debug-embed"] } sanitize-filename = "0.6.0" diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index 30243dad..97055a3d 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -9,6 +9,8 @@ use crate::{ wayland::handlers::compositor::FRAME_TIME_FILTER, }; use keyframe::{ease, functions::EaseInOutCubic}; +use resvg::{tiny_skia, usvg}; +use serde::Deserialize; use smithay::{ backend::{ allocator::Fourcc, @@ -52,14 +54,43 @@ use xcursor::{ static FALLBACK_CURSOR_DATA: &[u8] = include_bytes!("../../../resources/cursor.rgba"); +/// A single frame of a scalable SVG cursor: the parsed SVG plus its metadata. +#[derive(Debug, Clone)] +struct SvgFrame { + tree: usvg::Tree, + /// The nominal (logical) size the SVG is authored for. + nominal_size: f32, + /// Hotspot coordinates, in the SVG's own (nominal) coordinate space. + hotspot_x: f32, + hotspot_y: f32, + /// Delay to the next frame in milliseconds (0 for static cursors). + delay: u32, +} + +#[derive(Debug, Clone)] +enum CursorKind { + /// Legacy raster XCursor frames. + Xcursor(Vec), + /// Scalable SVG frames, rasterized on demand. + Svg(Vec), +} + #[derive(Debug, Clone)] pub struct Cursor { - icons: Vec, + kind: CursorKind, size: u32, } impl Cursor { pub fn load(theme: &CursorTheme, shape: CursorIcon, size: u32) -> Cursor { + // Prefer a scalable SVG cursor when the theme provides one. + if let Some(frames) = load_svg_icon(theme, shape) { + return Cursor { + kind: CursorKind::Svg(frames), + size, + }; + } + let icons = load_icon(theme, shape) .map_err(|err| warn!(?err, "Unable to load xcursor, using fallback cursor")) .or_else(|_| load_icon(theme, CursorIcon::Default)) @@ -76,44 +107,134 @@ impl Cursor { }] }); - Cursor { icons, size } + Cursor { + kind: CursorKind::Xcursor(icons), + size, + } } pub fn get_image(&self, scale: u32, millis: u32) -> Image { let size = self.size * scale; - frame(millis, size, &self.icons) + let idx = self.frame_index(size, millis); + self.render_frame(size, idx) + } + + /// Selects the index of the frame to display at nominal size `size` (in px) + /// and elapsed `millis`. + fn frame_index(&self, size: u32, millis: u32) -> usize { + match &self.kind { + CursorKind::Xcursor(images) => xcursor_frame_index(millis, size, images), + CursorKind::Svg(frames) => svg_frame_index(millis, frames), + } + } + + /// Produces the RGBA image for frame `idx`, rasterizing SVG cursors at the + /// requested nominal size `size` (in px). + fn render_frame(&self, size: u32, idx: usize) -> Image { + match &self.kind { + CursorKind::Xcursor(images) => images[idx].clone(), + CursorKind::Svg(frames) => rasterize_svg_frame(&frames[idx], size), + } } } -fn nearest_images(size: u32, images: &[Image]) -> impl Iterator { - // Follow the nominal size of the cursor to choose the nearest +/// Rasterize a scalable cursor frame at nominal pixel size `size`. Per the KDE +/// SVG cursor format, the SVG canvas and hotspot are scaled by +/// `size / nominal_size`. The returned [`Image`] uses the same premultiplied +/// byte order (BGRA / little-endian ARGB) as xcursor images. +fn rasterize_svg_frame(frame: &SvgFrame, size: u32) -> Image { + let factor = size as f32 / frame.nominal_size; + let svg_size = frame.tree.size(); + let width = ((svg_size.width() * factor).floor() as u32).max(1); + let height = ((svg_size.height() * factor).floor() as u32).max(1); + + let mut image = Image { + size, + width, + height, + xhot: (frame.hotspot_x * factor).floor() as u32, + yhot: (frame.hotspot_y * factor).floor() as u32, + delay: frame.delay, + pixels_rgba: Vec::new(), + pixels_argb: Vec::new(), // unused + }; + + match tiny_skia::Pixmap::new(width, height) { + Some(mut pixmap) => { + resvg::render( + &frame.tree, + tiny_skia::Transform::from_scale(factor, factor), + &mut pixmap.as_mut(), + ); + // tiny-skia produces premultiplied RGBA; xcursor images consume the + // raw little-endian ARGB byte order (premultiplied BGRA). Swap R<->B. + let mut pixels = pixmap.take(); + for px in pixels.as_chunks_mut::<4>().0 { + px.swap(0, 2); + } + image.pixels_rgba = pixels; + } + None => { + warn!(width, height, "Failed to allocate cursor pixmap"); + image.pixels_rgba = vec![0; (width as usize) * (height as usize) * 4]; + } + } + + image +} + +/// Indices (into `images`) of all frames sharing the resolution nearest to `size`. +fn nearest_image_indices(size: u32, images: &[Image]) -> Vec { + // Follow the nominal size of the cursor to choose the nearest. let nearest_image = images .iter() .min_by_key(|image| u32::abs_diff(size, image.size)) .unwrap(); + let (width, height) = (nearest_image.width, nearest_image.height); - images.iter().filter(move |image| { - image.width == nearest_image.width && image.height == nearest_image.height - }) + images + .iter() + .enumerate() + .filter(|(_, image)| image.width == width && image.height == height) + .map(|(i, _)| i) + .collect() } -fn frame(mut millis: u32, size: u32, images: &[Image]) -> Image { - let total = nearest_images(size, images).fold(0, |acc, image| acc + image.delay); +fn xcursor_frame_index(mut millis: u32, size: u32, images: &[Image]) -> usize { + let indices = nearest_image_indices(size, images); + let total: u32 = indices.iter().map(|&i| images[i].delay).sum(); if total == 0 { - millis = 0; - } else { - millis %= total; + return indices[0]; } + millis %= total; - for img in nearest_images(size, images) { - if millis <= img.delay { - return img.clone(); + for &i in &indices { + if millis <= images[i].delay { + return i; } - millis -= img.delay; + millis -= images[i].delay; } - unreachable!() + *indices.last().unwrap() +} + +fn svg_frame_index(mut millis: u32, frames: &[SvgFrame]) -> usize { + let total: u32 = frames.iter().map(|frame| frame.delay).sum(); + + if total == 0 { + return 0; + } + millis %= total; + + for (i, frame) in frames.iter().enumerate() { + if millis <= frame.delay { + return i; + } + millis -= frame.delay; + } + + frames.len() - 1 } #[derive(thiserror::Error, Debug)] @@ -175,6 +296,79 @@ fn load_icon(theme: &CursorTheme, shape: CursorIcon) -> Result, Error Err(Error::NoDefaultCursor) } +/// A frame entry in a `cursors_scalable//metadata.json` file, per the +/// KDE SVG cursor format specification. +#[derive(Debug, Clone, Deserialize)] +struct SvgCursorMeta { + filename: String, + nominal_size: f32, + hotspot_x: f32, + hotspot_y: f32, + /// Only present for animated cursors; defaults to 0 for static ones. + #[serde(default)] + delay: u32, +} + +/// Resolves a scalable (SVG) cursor for `shape` +fn load_svg_icon(theme: &CursorTheme, shape: CursorIcon) -> Option> { + let shape_name = shape.to_string(); + let options = usvg::Options::default(); + for name in cursor_aliases(&shape_name) + .iter() + .copied() + .chain(std::iter::once(shape_name.as_str())) + { + if let Some(dir) = theme.load_scalable(name) + && let Some(frames) = parse_svg_dir(&dir, &options) + { + return Some(frames); + } + } + + None +} + +/// Reads a `cursors_scalable/` directory (its `metadata.json` and the +/// referenced SVG files) into a list of parsed frames. +fn parse_svg_dir(dir: &std::path::Path, options: &usvg::Options) -> Option> { + let metadata = std::fs::read(dir.join("metadata.json")).ok()?; + let metas: Vec = match serde_json::from_slice(&metadata) { + Ok(metas) => metas, + Err(err) => { + warn!(?dir, ?err, "Malformed SVG cursor metadata"); + return None; + } + }; + + let mut frames = Vec::with_capacity(metas.len()); + for meta in metas { + let svg_path = dir.join(&meta.filename); + let svg_data = match std::fs::read(&svg_path) { + Ok(data) => data, + Err(err) => { + warn!(?svg_path, ?err, "Unable to read SVG cursor"); + break; + } + }; + let tree = match usvg::Tree::from_data(&svg_data, options) { + Ok(tree) => tree, + Err(err) => { + warn!(?svg_path, ?err, "Unable to parse SVG cursor"); + break; + } + }; + frames.push(SvgFrame { + tree, + nominal_size: meta.nominal_size, + hotspot_x: meta.hotspot_x, + hotspot_y: meta.hotspot_y, + delay: meta.delay, + }); + } + + (!frames.is_empty()).then_some(frames) +} + render_elements! { pub CursorRenderElement where R: ImportAll + ImportMem + AsGlowRenderer, R::TextureId: Send; Static=MemoryRenderBufferRenderElement, @@ -266,7 +460,9 @@ pub struct CursorStateInner { cursors: HashMap, current_image: Option, - image_cache: Vec<(Image, MemoryRenderBuffer)>, + /// Rasterized cursor frames keyed by `(shape, pixel size, frame index)` so + /// each SVG frame is rasterized (and uploaded) at most once per size. + image_cache: Vec<((CursorIcon, u32, usize), Image, MemoryRenderBuffer)>, hidden: bool, idle_timer: Option, @@ -320,9 +516,11 @@ impl CursorStateInner { } pub fn get_named_cursor(&mut self, shape: CursorIcon) -> &Cursor { + let cursor_theme = &self.cursor_theme; + let cursor_size = self.cursor_size; self.cursors .entry(shape) - .or_insert_with(|| Cursor::load(&self.cursor_theme, shape, self.cursor_size)) + .or_insert_with(|| Cursor::load(cursor_theme, shape, cursor_size)) } pub fn size(&self) -> u32 { @@ -520,30 +718,47 @@ pub fn draw_cursor( } let integer_scale = (scale.x.max(scale.y) * buffer_scale).ceil() as u32; - let frame = state - .get_named_cursor(current_cursor) - .get_image(integer_scale, time.as_millis()); - let actual_scale = (frame.size / state.size()).max(1); + let size_px = state.size() * integer_scale; - let pointer_images = &mut state.image_cache; - let maybe_image = pointer_images - .iter() - .find_map(|(image, texture)| if image == &frame { Some(texture) } else { None }); - let pointer_image = match maybe_image { - Some(image) => image, - None => { - let buffer = MemoryRenderBuffer::from_slice( - &frame.pixels_rgba, - Fourcc::Argb8888, - (frame.width as i32, frame.height as i32), - actual_scale as i32, - Transform::Normal, - None, - ); - pointer_images.push((frame.clone(), buffer)); - pointer_images.last().map(|(_, i)| i).unwrap() - } + // Pick the frame to display without rasterizing, so a cache hit avoids + // any SVG rendering. The `&Cursor` borrow is scoped to this block. + let frame_idx = { + let cursor = state.get_named_cursor(current_cursor); + cursor.frame_index(size_px, time.as_millis()) }; + let key = (current_cursor, size_px, frame_idx); + + // Rasterize and upload this (shape, size, frame) only if not cached. + if !state + .image_cache + .iter() + .any(|(cached, _, _)| *cached == key) + { + let frame = { + let cursor = state.get_named_cursor(current_cursor); + cursor.render_frame(size_px, frame_idx) + }; + let actual_scale = (frame.size / state.size()).max(1); + let buffer = MemoryRenderBuffer::from_slice( + &frame.pixels_rgba, + Fourcc::Argb8888, + (frame.width as i32, frame.height as i32), + actual_scale as i32, + Transform::Normal, + None, + ); + state.image_cache.push((key, frame, buffer)); + } + + let (frame, pointer_image) = { + let (_, frame, buffer) = state + .image_cache + .iter() + .find(|(cached, _, _)| *cached == key) + .unwrap(); + (frame.clone(), buffer.clone()) + }; + let actual_scale = (frame.size / state.size()).max(1); let hotspot = Point::::from((frame.xhot as i32, frame.yhot as i32)) .to_logical( @@ -558,7 +773,7 @@ pub fn draw_cursor( MemoryRenderBufferRenderElement::from_buffer( renderer, location.to_physical(scale), - pointer_image, + &pointer_image, None, None, None, From 80b8eb47d733aca855380ec249fa5fe34fd6ee4e Mon Sep 17 00:00:00 2001 From: Hojjat Date: Fri, 21 Aug 2026 14:46:49 -0600 Subject: [PATCH 32/80] improv: round up and down sample cursor sizes to limit cache --- src/backend/render/cursor.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index 97055a3d..7a0861e5 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -497,6 +497,8 @@ const SHAKE_SAME_SIGN_TOLERANCE: f64 = 1.0; const SHAKE_HOLD: Duration = Duration::from_millis(2000); /// Extra magnification added by each shake, growing from the normal cursor size. const OVER_MAGNIFICATION: f32 = 1.0; +/// Upper bound on the nominal size (in px) a cursor frame is rasterized at. +const MAX_RASTER_SIZE: u32 = 512; /// Duration of the grow/shrink animation. const MAGNIFICATION_ANIM: Duration = Duration::from_millis(200); @@ -682,6 +684,21 @@ impl Default for CursorStateInner { } } +/// Pick the size a cursor frame is rasterized at, given the size the output wants +/// (`needed`) and the size it would want unmagnified (`base`). +/// +/// Rasterizations are restricted to `base * 2^n`, rounded up, and clamped to +/// [`MAX_RASTER_SIZE`]. +fn raster_size(needed: u32, base: u32) -> u32 { + let base = base.max(1); + let cap = MAX_RASTER_SIZE.max(base); + let mut rung = base; + while rung < needed && rung.saturating_mul(2) <= cap { + rung *= 2; + } + rung +} + #[profiling::function] pub fn draw_cursor( renderer: &mut R, @@ -717,8 +734,10 @@ pub fn draw_cursor( return; } - let integer_scale = (scale.x.max(scale.y) * buffer_scale).ceil() as u32; - let size_px = state.size() * integer_scale; + let output_scale = scale.x.max(scale.y); + let integer_scale = (output_scale * buffer_scale).ceil() as u32; + let unmagnified_px = state.size() * (output_scale.ceil() as u32); + let size_px = raster_size(state.size() * integer_scale, unmagnified_px); // Pick the frame to display without rasterizing, so a cache hit avoids // any SVG rendering. The `&Cursor` borrow is scoped to this block. From 31827ed2409f92d3bc224d3ce601c6f2136183fb Mon Sep 17 00:00:00 2001 From: Hojjat Date: Mon, 24 Aug 2026 10:57:29 -0600 Subject: [PATCH 33/80] improv: clear image_cache on refresh --- src/backend/render/cursor.rs | 86 ++++++++++++++++++++++++------------ src/shell/mod.rs | 23 ++++++++++ 2 files changed, 80 insertions(+), 29 deletions(-) diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index 7a0861e5..8e14d1be 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -460,9 +460,7 @@ pub struct CursorStateInner { cursors: HashMap, current_image: Option, - /// Rasterized cursor frames keyed by `(shape, pixel size, frame index)` so - /// each SVG frame is rasterized (and uploaded) at most once per size. - image_cache: Vec<((CursorIcon, u32, usize), Image, MemoryRenderBuffer)>, + image_cache: Vec, hidden: bool, idle_timer: Option, @@ -476,6 +474,15 @@ pub struct CursorStateInner { magnification: f32, anim_from: f32, anim_start: Option, + rest_started: Option, +} + +/// A rasterized cursor frame, keyed by `(shape, pixel size, frame index)`. +struct CachedFrame { + key: (CursorIcon, u32, usize), + image: Image, + buffer: MemoryRenderBuffer, + unmagnified: bool, } /// One sampled pointer position on the recent motion path. @@ -485,6 +492,9 @@ struct PathSample { time: Instant, } +/// How long everything must stay unmagnified before the enlarged frames go. +const MAGNIFIED_FRAME_GRACE: Duration = Duration::from_secs(10); + /// How far back the motion path is considered when looking for a shake. const SHAKE_INTERVAL: Duration = Duration::from_millis(1000); /// Path-length / bounding-box-diagonal ratio required to count as a shake. @@ -529,6 +539,21 @@ impl CursorStateInner { self.cursor_size } + /// Drop the rasterizations only a magnified cursor needed, once nothing has + /// magnified it for [`MAGNIFIED_FRAME_GRACE`]. + pub fn drop_magnified_frames(&mut self, now: Instant, zoomed: bool) { + if zoomed || self.is_magnifying() { + self.rest_started = None; + return; + } + let rest_started = *self.rest_started.get_or_insert(now); + if now.duration_since(rest_started) < MAGNIFIED_FRAME_GRACE { + return; + } + + self.image_cache.retain(|frame| frame.unmagnified); + } + /// Feed one relative-motion event into the shake detector. pub fn detect_shake(&mut self, delta: Point, now: Instant) { // Drop samples that have aged out of the time window. @@ -680,6 +705,7 @@ impl Default for CursorStateInner { magnification: 1.0, anim_from: 1.0, anim_start: None, + rest_started: None, } } } @@ -748,34 +774,36 @@ pub fn draw_cursor( let key = (current_cursor, size_px, frame_idx); // Rasterize and upload this (shape, size, frame) only if not cached. - if !state - .image_cache - .iter() - .any(|(cached, _, _)| *cached == key) - { - let frame = { - let cursor = state.get_named_cursor(current_cursor); - cursor.render_frame(size_px, frame_idx) - }; - let actual_scale = (frame.size / state.size()).max(1); - let buffer = MemoryRenderBuffer::from_slice( - &frame.pixels_rgba, - Fourcc::Argb8888, - (frame.width as i32, frame.height as i32), - actual_scale as i32, - Transform::Normal, - None, - ); - state.image_cache.push((key, frame, buffer)); - } + let index = match state.image_cache.iter().position(|frame| frame.key == key) { + Some(index) => index, + None => { + let image = { + let cursor = state.get_named_cursor(current_cursor); + cursor.render_frame(size_px, frame_idx) + }; + let actual_scale = (image.size / state.size()).max(1); + let buffer = MemoryRenderBuffer::from_slice( + &image.pixels_rgba, + Fourcc::Argb8888, + (image.width as i32, image.height as i32), + actual_scale as i32, + Transform::Normal, + None, + ); + state.image_cache.push(CachedFrame { + key, + image, + buffer, + unmagnified: size_px == unmagnified_px, + }); + state.image_cache.len() - 1 + } + }; let (frame, pointer_image) = { - let (_, frame, buffer) = state - .image_cache - .iter() - .find(|(cached, _, _)| *cached == key) - .unwrap(); - (frame.clone(), buffer.clone()) + let entry = &mut state.image_cache[index]; + entry.unmagnified |= size_px == unmagnified_px; + (entry.image.clone(), entry.buffer.clone()) }; let actual_scale = (frame.size / state.size()).max(1); diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 0baa3713..009c98dc 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1614,6 +1614,29 @@ impl Common { a11y_keyboard_monitor.refresh(); } self.image_copy_capture_state.cleanup(); + self.cleanup_cursor_images(); + } + + /// Release the enlarged cursor frames a finished shake or zoom left behind. + fn cleanup_cursor_images(&mut self) { + let shell = self.shell.read(); + let zoomed = shell.zoom_state.as_ref().is_some_and(|zoom_state| { + shell + .outputs() + .any(|output| zoom_state.animating_level(output) > 1.0) + }); + let now = Instant::now(); + for seat in shell.seats.iter() { + if let Some(cursor_state) = seat + .user_data() + .get::() + { + cursor_state + .lock() + .unwrap() + .drop_magnified_frames(now, zoomed); + } + } } pub fn refresh_idle_inhibit(&mut self) { From 5c93094574b31800caaa27255caac4a7253f24ad Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 19 Aug 2026 12:40:05 +0200 Subject: [PATCH 34/80] chore: update smithay --- Cargo.lock | 53 +++------- Cargo.toml | 2 +- src/config/mod.rs | 8 +- src/input/actions.rs | 5 +- src/input/mod.rs | 107 ++++++++++---------- src/shell/element/mod.rs | 4 +- src/shell/element/stack.rs | 6 +- src/shell/element/surface.rs | 3 +- src/shell/element/window.rs | 6 +- src/shell/focus/mod.rs | 5 +- src/shell/focus/target.rs | 15 +-- src/shell/grabs/menu/default.rs | 13 +-- src/shell/grabs/moving.rs | 4 +- src/shell/layout/tiling/grabs/resize.rs | 4 +- src/shell/layout/tiling/grabs/swap.rs | 4 +- src/shell/zoom.rs | 4 +- src/utils/iced/mod.rs | 6 +- src/wayland/handlers/compositor.rs | 17 ++-- src/wayland/handlers/pointer_constraints.rs | 52 ++++++---- src/wayland/handlers/pointer_warp.rs | 2 +- src/wayland/handlers/toplevel_management.rs | 3 +- src/xwayland.rs | 10 +- 22 files changed, 168 insertions(+), 165 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b8be66ca..859203e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1189,7 +1189,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1503,7 +1503,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2690,7 +2690,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2813,7 +2813,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1886916523694cd6ea3d175f03a1e5010699a2a4cc13696d83d7bea1d80638" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3429,7 +3429,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4167,20 +4167,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", + "rand_chacha", "rand_core 0.6.4", ] -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", -] - [[package]] name = "rand" version = "0.10.0" @@ -4202,16 +4192,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", -] - [[package]] name = "rand_core" version = "0.6.4" @@ -4221,15 +4201,6 @@ dependencies = [ "getrandom 0.2.17", ] -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - [[package]] name = "rand_core" version = "0.10.0" @@ -4525,7 +4496,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4820,7 +4791,7 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/smithay/smithay.git?rev=5fb12b8#5fb12b87407b3680135c45d94214c5f1b1d0fbea" +source = "git+https://github.com/smithay/smithay.git?rev=e3d461a#e3d461a057ba244d213a8498ec372b0799cca103" dependencies = [ "aliasable", "appendlist", @@ -4849,7 +4820,7 @@ dependencies = [ "pkg-config", "portable-atomic", "profiling", - "rand 0.9.2", + "rand 0.10.0", "reis", "rustix 1.1.4", "scopeguard", @@ -5115,7 +5086,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5414,7 +5385,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f7c95348f20c1c913d72157b3c6dee6ea3e30b3d19502c5a7f6d3f160dacbf" dependencies = [ "cc", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -6134,7 +6105,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d807d369..23fe2775 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -146,4 +146,4 @@ lto = "fat" cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } [patch.crates-io] -smithay = { git = "https://github.com/smithay/smithay.git", rev = "5fb12b8" } +smithay = { git = "https://github.com/smithay/smithay.git", rev = "e3d461a" } diff --git a/src/config/mod.rs b/src/config/mod.rs index 93b53374..abef562b 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -14,8 +14,11 @@ use cosmic_config::{ConfigGet, CosmicConfigEntry}; use cosmic_settings_config::window_rules::ApplicationException; use cosmic_settings_config::{Shortcuts, shortcuts, window_rules}; use serde::{Deserialize, Serialize}; -use smithay::utils::{Clock, Monotonic}; use smithay::wayland::xdg_activation::XdgActivationState; +use smithay::{ + backend::input::InputTime, + utils::{Clock, Monotonic}, +}; pub use smithay::{ backend::input::{self as smithay_input, KeyState}, input::keyboard::{Keysym, ModifiersState, keysyms as KeySyms}, @@ -770,13 +773,12 @@ pub fn change_modifier_state( const X11_KEYCODE_OFFSET: u32 = 8; let mut input = |key_state, scan_code| { - let time = state.common.clock.now().as_millis(); let _ = keyboard.input( state, smithay_input::Keycode::new(scan_code + X11_KEYCODE_OFFSET), key_state, SERIAL_COUNTER.next_serial(), - time, + InputTime::now(), |_, _, _| smithay::input::keyboard::FilterResult::<()>::Forward, ); }; diff --git a/src/input/actions.rs b/src/input/actions.rs index 8a5ef84c..8639ba62 100644 --- a/src/input/actions.rs +++ b/src/input/actions.rs @@ -18,6 +18,7 @@ use cosmic_config::ConfigSet; use cosmic_settings_config::shortcuts; use cosmic_settings_config::shortcuts::action::{Direction, FocusDirection}; use smithay::{ + backend::input::InputTime, input::{Seat, pointer::MotionEvent}, utils::{Point, Serial}, }; @@ -45,7 +46,7 @@ impl State { backend_id: &InputBackendId, seat: &Seat, serial: Serial, - time: u32, + time: InputTime, pattern: shortcuts::Binding, direction: Option, ) { @@ -150,7 +151,7 @@ impl State { backend_id: &InputBackendId, seat: &Seat, serial: Serial, - time: u32, + time: InputTime, pattern: shortcuts::Binding, direction: Option, propagate: bool, diff --git a/src/input/mod.rs b/src/input/mod.rs index 0e307079..d619a525 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -42,9 +42,9 @@ use smithay::{ backend::input::{ AbsolutePositionEvent, Axis, AxisRelativeDirection, AxisSource, Device, DeviceCapability, GestureBeginEvent, GestureEndEvent, GesturePinchUpdateEvent as _, - GestureSwipeUpdateEvent as _, InputBackend, InputEvent, KeyState, PointerAxisEvent, - ProximityState, TabletToolButtonEvent, TabletToolEvent, TabletToolProximityEvent, - TabletToolTipEvent, TabletToolTipState, TouchEvent, + GestureSwipeUpdateEvent as _, InputBackend, InputEvent, InputTime, KeyState, + PointerAxisEvent, ProximityState, TabletToolButtonEvent, TabletToolEvent, + TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState, TouchEvent, }, desktop::{PopupKeyboardGrab, WindowSurfaceType, utils::under_from_surface_tree}, input::{ @@ -271,7 +271,7 @@ impl State { trace!(?keycode, ?state, "key"); let serial = SERIAL_COUNTER.next_serial(); - let time = Event::time_msec(&event); + let time = Event::time(&event); let keyboard = seat.get_keyboard().unwrap(); let previous_modifiers = keyboard.modifier_state(); if let Some((action, pattern)) = keyboard @@ -397,7 +397,7 @@ impl State { &RelativeMotionEvent { delta: event.delta(), delta_unaccel: event.delta_unaccel(), - utime: event.time(), + time: event.time(), }, ); @@ -638,7 +638,7 @@ impl State { &MotionEvent { location: position.as_logical(), serial, - time: event.time_msec(), + time: event.time(), }, ); ptr.frame(self); @@ -753,7 +753,7 @@ impl State { &MotionEvent { location: position.as_logical(), serial, - time: event.time_msec(), + time: event.time(), }, ); ptr.frame(self); @@ -1005,7 +1005,7 @@ impl State { button, event.state(), serial, - event.time_msec(), + event.time(), ); } @@ -1017,12 +1017,12 @@ impl State { button, state: event.state(), serial, - time: event.time_msec(), + time: event.time(), }, ); ptr.frame(self); } else if event.state() == ButtonState::Released { - ptr.unset_grab(self, serial, event.time_msec()) + ptr.unset_grab(self, serial, event.time()) } } InputEvent::PointerAxis { event, .. } => { @@ -1073,7 +1073,7 @@ impl State { self.update_zoom(&seat, change, event.source() == AxisSource::Wheel); } } else { - let mut frame = AxisFrame::new(event.time_msec()).source(event.source()); + let mut frame = AxisFrame::new(event.time()).source(event.source()); let horizontal_amount = event .amount(Axis::Horizontal) .or_else(|| Some(event.amount_v120(Axis::Horizontal)? * 15.0 / 120.)); @@ -1142,7 +1142,7 @@ impl State { self, &GestureSwipeBeginEvent { serial, - time: event.time_msec(), + time: event.time(), fingers: event.fingers(), }, ); @@ -1163,7 +1163,7 @@ impl State { if let Some(ref mut gesture_state) = self.common.gesture_state { let first_update = gesture_state.update( event.delta(), - Duration::from_millis(event.time_msec() as u64), + Duration::from_millis(event.time().millis() as u64), ); // Decide on action if first update if first_update { @@ -1239,7 +1239,7 @@ impl State { pointer.gesture_swipe_update( self, &GestureSwipeUpdateEvent { - time: event.time_msec(), + time: event.time(), delta: event.delta(), }, ); @@ -1288,7 +1288,7 @@ impl State { self, &GestureSwipeEndEvent { serial, - time: event.time_msec(), + time: event.time(), cancelled: event.cancelled(), }, ); @@ -1311,7 +1311,7 @@ impl State { self, &GesturePinchBeginEvent { serial, - time: event.time_msec(), + time: event.time(), fingers: event.fingers(), }, ); @@ -1331,7 +1331,7 @@ impl State { pointer.gesture_pinch_update( self, &GesturePinchUpdateEvent { - time: event.time_msec(), + time: event.time(), delta: event.delta(), scale: event.scale(), rotation: event.rotation(), @@ -1355,7 +1355,7 @@ impl State { self, &GesturePinchEndEvent { serial, - time: event.time_msec(), + time: event.time(), cancelled: event.cancelled(), }, ); @@ -1377,7 +1377,7 @@ impl State { self, &GestureHoldBeginEvent { serial, - time: event.time_msec(), + time: event.time(), fingers: event.fingers(), }, ); @@ -1399,7 +1399,7 @@ impl State { self, &GestureHoldEndEvent { serial, - time: event.time_msec(), + time: event.time(), cancelled: event.cancelled(), }, ); @@ -1456,7 +1456,7 @@ impl State { slot: event.slot(), location: position.as_logical(), serial, - time: event.time_msec(), + time: event.time(), }, ); } @@ -1505,7 +1505,7 @@ impl State { &TouchMotionEvent { slot: event.slot(), location: position.as_logical(), - time: event.time_msec(), + time: event.time(), }, ); } @@ -1531,7 +1531,7 @@ impl State { self, &UpEvent { slot: event.slot(), - time: event.time_msec(), + time: event.time(), serial, }, ); @@ -1596,7 +1596,7 @@ impl State { &MotionEvent { location: position.as_logical(), serial: SERIAL_COUNTER.next_serial(), - time: self.common.clock.now().as_millis(), + time: InputTime::now(), }, ); @@ -1627,11 +1627,11 @@ impl State { &tool::MotionEvent { location: position.as_logical(), serial: SERIAL_COUNTER.next_serial(), - time: event.time_msec(), + time: event.time(), }, ); - tool.frame(self, event.time_msec()); + tool.frame(self, event.time()); } } } @@ -1665,7 +1665,7 @@ impl State { &MotionEvent { location: position.as_logical(), serial: SERIAL_COUNTER.next_serial(), - time: self.common.clock.now().as_millis(), + time: InputTime::now(), }, ); @@ -1706,7 +1706,7 @@ impl State { location: position.as_logical(), axis: Some(frame), serial: SERIAL_COUNTER.next_serial(), - time: event.time_msec(), + time: event.time(), }, ) } @@ -1714,12 +1714,12 @@ impl State { self, &tool::ProximityOutEvent { serial, - time: event.time_msec(), + time: event.time(), }, ), } - tool.frame(self, event.time_msec()); + tool.frame(self, event.time()); } } } @@ -1742,7 +1742,7 @@ impl State { self, &tool::DownEvent { serial, - time: event.time_msec(), + time: event.time(), }, ); } @@ -1751,13 +1751,13 @@ impl State { self, &tool::UpEvent { serial, - time: event.time_msec(), + time: event.time(), }, ); } } - tool.frame(self, event.time_msec()); + tool.frame(self, event.time()); } } } @@ -1779,11 +1779,11 @@ impl State { button: event.button(), state: event.button_state(), serial: SERIAL_COUNTER.next_serial(), - time: event.time_msec(), + time: event.time(), }, ); - tool.frame(self, event.time_msec()); + tool.frame(self, event.time()); } } } @@ -1886,7 +1886,7 @@ impl State { let Some(pointer) = seat.get_pointer() else { return; }; - let time = self.common.clock.now().as_millis(); + let time = InputTime::now(); for button in buttons { let serial = SERIAL_COUNTER.next_serial(); pointer.button( @@ -1948,7 +1948,7 @@ impl State { modifiers: &ModifiersState, handle: KeysymHandle<'_>, serial: Serial, - time: u32, + time: InputTime, keycode: Keycode, key_state: KeyState, previous_modifiers: ModifiersState, @@ -2008,7 +2008,7 @@ impl State { return; }; let serial = SERIAL_COUNTER.next_serial(); - let time = self.common.clock.now().as_millis(); + let time = InputTime::now(); let previous_modifiers = keyboard.modifier_state(); let result = keyboard .input_from_source( @@ -2157,7 +2157,7 @@ impl State { serial: Serial, keycode: Keycode, key_state: KeyState, - time: u32, + time: InputTime, ) -> FilterResult> { // Pre-compute for layout-agnostic shortcut matching let raw_syms = handle.raw_syms(); @@ -2322,7 +2322,9 @@ impl State { &backend_id_clone, &seat_clone, serial, - time.overflowing_add(duration as u32).0, + InputTime::from_millis( + time.millis().overflowing_add(duration as u32).0, + ), key_pattern_clone.clone(), None, ); @@ -2374,7 +2376,7 @@ impl State { 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(); + let time = InputTime::now(); keyboard.input( state, key_code, @@ -2900,6 +2902,7 @@ impl State { surface: &WlSurface, pointer: &PointerHandle, mut location: Point, + constraint: Option<&PointerConstraint>, ) { let Some(client) = surface.client() else { return; @@ -2924,15 +2927,13 @@ impl State { 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_floor(); - return region.contains(point_in_surface); - } - true - }) + if let Some(constraint) = constraint + && let Some(region) = constraint.region() + { + let point_in_surface = (p - surface_offset.to_f64()).to_i32_floor(); + return region.contains(point_in_surface); + } + true }; let workspace_origin = output.geometry().loc.to_f64(); @@ -2956,14 +2957,14 @@ impl State { let serial = SERIAL_COUNTER.next_serial(); let under = State::surface_under(point, &output, &self.common.shell.write()) .map(|(target, pos)| (target, pos.as_logical())); - let time = self.common.clock.now(); + let time = InputTime::now(); pointer.relative_motion( self, under.clone(), &RelativeMotionEvent { delta: (0., 0.).into(), delta_unaccel: (0., 0.).into(), - utime: time.as_micros(), + time, }, ); pointer.motion( @@ -2972,7 +2973,7 @@ impl State { &MotionEvent { location: point.as_logical(), serial, - time: time.as_millis(), + time, }, ); pointer.frame(self); diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 14736719..d4743b7f 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -12,7 +12,7 @@ use id_tree::NodeId; use smithay::{ backend::{ drm::DrmNode, - input::KeyState, + input::{InputTime, KeyState}, renderer::{ element::{ Element, Kind, RenderElement, UnderlyingStorage, @@ -1012,7 +1012,7 @@ impl KeyboardTarget for CosmicMapped { key: KeysymHandle<'_>, state: KeyState, serial: Serial, - time: u32, + time: InputTime, ) { match &self.element { CosmicMappedInternal::Stack(s) => { diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index d466f4e0..2fed0977 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -38,7 +38,7 @@ use shortcuts::action::{Direction, FocusDirection}; use smithay::{ backend::{ drm::DrmNode, - input::KeyState, + input::{InputTime, KeyState}, renderer::{ ImportAll, ImportMem, Renderer, element::{Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage}, @@ -1557,7 +1557,7 @@ impl KeyboardTarget for CosmicStack { key: KeysymHandle<'_>, state: KeyState, serial: Serial, - time: u32, + time: InputTime, ) { let active = self.keyboard_leave_if_previous(seat, data, serial); self.0.with_program(|p| { @@ -1726,7 +1726,7 @@ impl PointerTarget for CosmicStack { } } - fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) { + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: InputTime) { self.0.with_program(|p| { let mut cursor_state = seat .user_data() diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 99bb2f78..a7fd922d 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -21,6 +21,7 @@ use std::{ use smithay::{ backend::{ drm::DrmNode, + input::InputTime, renderer::{ ImportAll, Renderer, buffer_has_alpha, element::{Kind, RenderElementStates, surface::KindEvaluation}, @@ -1084,7 +1085,7 @@ impl KeyboardTarget for CosmicSurface { key: KeysymHandle<'_>, state: smithay::backend::input::KeyState, serial: smithay::utils::Serial, - time: u32, + time: InputTime, ) { match self.0.underlying_surface() { WindowSurface::Wayland(toplevel) => { diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 86c2ce7f..53b2caee 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -21,7 +21,7 @@ use cosmic_comp_config::AppearanceConfig; use smithay::{ backend::{ drm::DrmNode, - input::KeyState, + input::{InputTime, KeyState}, renderer::{ ImportAll, ImportMem, Renderer, element::{Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage}, @@ -985,7 +985,7 @@ impl KeyboardTarget for CosmicWindow { key: KeysymHandle<'_>, state: KeyState, serial: Serial, - time: u32, + time: InputTime, ) { self.0 .with_program(|p| KeyboardTarget::key(&p.window, seat, data, key, state, serial, time)) @@ -1124,7 +1124,7 @@ impl PointerTarget for CosmicWindow { } } - fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) { + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: InputTime) { self.0.with_program(|p| { let cursor_state = seat.user_data().get::().unwrap(); cursor_state.lock().unwrap().unset_shape(); diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index cab37a85..f631372e 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -6,6 +6,7 @@ use crate::{ }; use indexmap::IndexSet; use smithay::{ + backend::input::InputTime, desktop::{PopupUngrabStrategy, layer_map_for_output}, input::{Seat, pointer::MotionEvent}, output::Output, @@ -419,7 +420,7 @@ fn update_focus_state( &MotionEvent { location: new_pos.as_logical(), serial: SERIAL_COUNTER.next_serial(), - time: 0, + time: InputTime::now(), }, ); } @@ -768,7 +769,7 @@ fn update_pointer_focus(state: &mut State, seat: &Seat) { &MotionEvent { location: pointer.current_location(), serial: SERIAL_COUNTER.next_serial(), - time: state.common.clock.now().as_millis(), + time: InputTime::now(), }, ); } diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index 80b1a34f..56af0ccc 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -16,7 +16,7 @@ use crate::{ }; use id_tree::NodeId; use smithay::{ - backend::input::KeyState, + backend::input::{InputTime, KeyState}, desktop::{LayerSurface, PopupKind, WindowSurface, WindowSurfaceType, space::SpaceElement}, input::{ Seat, @@ -272,9 +272,10 @@ impl PointerFocusTarget { None }; - let cursor_hotspot = if let Some(CursorGeometry { hotspot, .. }) = - seat.cursor_geometry((0.0, 0.0), Duration::from_millis(event.time as u64).into()) - { + let cursor_hotspot = if let Some(CursorGeometry { hotspot, .. }) = seat.cursor_geometry( + (0.0, 0.0), + Duration::from_millis(event.time.millis() as u64).into(), + ) { hotspot } else { Point::from((0, 0)) @@ -417,7 +418,7 @@ impl PointerTarget for PointerFocusTarget { fn frame(&self, seat: &Seat, data: &mut State) { self.inner_pointer_target().frame(seat, data); } - fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) { + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: InputTime) { let toplevel = self.toplevel(&data.common.shell.read()); if let Some(element) = toplevel { for session in element.cursor_sessions() { @@ -593,7 +594,7 @@ impl DndFocus for PointerFocusTarget { offer: Option<&mut CosmicOfferData>, seat: &Seat, location: Point, - time: u32, + time: InputTime, ) { match self { PointerFocusTarget::WlSurface { surface, .. } => { @@ -695,7 +696,7 @@ impl KeyboardTarget for KeyboardFocusTarget { key: KeysymHandle<'_>, state: KeyState, serial: Serial, - time: u32, + time: InputTime, ) { if let Some(inner) = self.inner_keyboard_target() { inner.key(seat, data, key, state, serial, time); diff --git a/src/shell/grabs/menu/default.rs b/src/shell/grabs/menu/default.rs index c7418b6d..81673efd 100644 --- a/src/shell/grabs/menu/default.rs +++ b/src/shell/grabs/menu/default.rs @@ -1,7 +1,8 @@ use cosmic_settings_config::shortcuts::Action; use smithay::{ - input::pointer::MotionEvent, reexports::wayland_server::protocol::wl_surface::WlSurface, - utils::SERIAL_COUNTER, wayland::seat::WaylandFocus, + backend::input::InputTime, input::pointer::MotionEvent, + reexports::wayland_server::protocol::wl_surface::WlSurface, utils::SERIAL_COUNTER, + wayland::seat::WaylandFocus, }; use crate::{ @@ -398,7 +399,7 @@ pub fn window_items( &MotionEvent { location: loc.as_logical().to_f64(), serial, - time: state.common.clock.now().as_millis(), + time: InputTime::now(), }, ); pointer.frame(state); @@ -433,7 +434,7 @@ pub fn window_items( &MotionEvent { location: loc.as_logical().to_f64(), serial, - time: state.common.clock.now().as_millis(), + time: InputTime::now(), }, ); pointer.frame(state); @@ -468,7 +469,7 @@ pub fn window_items( &MotionEvent { location: loc.as_logical().to_f64(), serial, - time: state.common.clock.now().as_millis(), + time: InputTime::now(), }, ); pointer.frame(state); @@ -503,7 +504,7 @@ pub fn window_items( &MotionEvent { location: loc.as_logical().to_f64(), serial, - time: state.common.clock.now().as_millis(), + time: InputTime::now(), }, ); pointer.frame(state); diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index d8714830..05923bc7 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -20,7 +20,7 @@ use smallvec::SmallVec; use smithay::{ backend::{ drm::DrmNode, - input::ButtonState, + input::{ButtonState, InputTime}, renderer::{ ImportAll, ImportMem, element::{RenderElement, utils::RescaleRenderElement}, @@ -956,7 +956,7 @@ impl Drop for MoveGrab { &MotionEvent { location: pointer.current_location(), serial, - time: state.common.clock.now().as_millis(), + time: InputTime::now(), }, ); } diff --git a/src/shell/layout/tiling/grabs/resize.rs b/src/shell/layout/tiling/grabs/resize.rs index 9b135119..91873793 100644 --- a/src/shell/layout/tiling/grabs/resize.rs +++ b/src/shell/layout/tiling/grabs/resize.rs @@ -11,7 +11,7 @@ use crate::{ }; use id_tree::{NodeId, Tree}; use smithay::{ - backend::input::ButtonState, + backend::input::{ButtonState, InputTime}, input::{ Seat, pointer::{ @@ -65,7 +65,7 @@ impl PointerTarget for ResizeForkTarget { seat: &Seat, _data: &mut State, _serial: smithay::utils::Serial, - _time: u32, + _time: InputTime, ) { let user_data = seat.user_data(); let cursor_state = user_data.get::().unwrap(); diff --git a/src/shell/layout/tiling/grabs/swap.rs b/src/shell/layout/tiling/grabs/swap.rs index afe93cf0..9ac531d3 100644 --- a/src/shell/layout/tiling/grabs/swap.rs +++ b/src/shell/layout/tiling/grabs/swap.rs @@ -1,6 +1,6 @@ use cosmic_settings_config::shortcuts; use smithay::{ - backend::input::{KeyState, Keycode}, + backend::input::{InputTime, KeyState, Keycode}, input::{ Seat, SeatHandler, keyboard::{ @@ -38,7 +38,7 @@ impl KeyboardGrab for SwapWindowGrab { state: KeyState, modifiers: Option, serial: Serial, - time: u32, + time: InputTime, ) { if !matches!(&data.common.shell.read().overview_mode.active_trigger(), Some(Trigger::KeyboardSwap(_, d)) if d == &self.desc) { diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index 0c0e9340..cfe84f06 100644 --- a/src/shell/zoom.rs +++ b/src/shell/zoom.rs @@ -11,7 +11,7 @@ use cosmic_comp_config::{ZoomConfig, ZoomMovement}; use cosmic_config::ConfigSet; use keyframe::{ease, functions::Linear}; use smithay::{ - backend::renderer::ImportMem, + backend::{input::InputTime, renderer::ImportMem}, desktop::space::SpaceElement, input::{ Seat, @@ -1023,7 +1023,7 @@ impl PointerTarget for ZoomFocusTarget { } } - fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) { + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: InputTime) { match self { ZoomFocusTarget::Main(elem) => PointerTarget::leave(elem, seat, data, serial, time), ZoomFocusTarget::Menu(elem) => PointerTarget::leave(elem, seat, data, serial, time), diff --git a/src/utils/iced/mod.rs b/src/utils/iced/mod.rs index 2bfe888d..94b27ea7 100644 --- a/src/utils/iced/mod.rs +++ b/src/utils/iced/mod.rs @@ -31,7 +31,7 @@ use ordered_float::OrderedFloat; use smithay::{ backend::{ allocator::Fourcc, - input::{ButtonState, KeyState}, + input::{ButtonState, InputTime, KeyState}, renderer::{ ImportMem, element::{ @@ -581,7 +581,7 @@ impl PointerTarget for IcedEle _seat: &Seat, _data: &mut crate::state::State, _serial: Serial, - _time: u32, + _time: InputTime, ) { let mut internal = self.0.lock().unwrap(); internal @@ -784,7 +784,7 @@ impl KeyboardTarget for IcedEl _key: KeysymHandle<'_>, _state: KeyState, _serial: Serial, - _time: u32, + _time: InputTime, ) { // TODO convert keys } diff --git a/src/wayland/handlers/compositor.rs b/src/wayland/handlers/compositor.rs index 12bc2e56..ed40448b 100644 --- a/src/wayland/handlers/compositor.rs +++ b/src/wayland/handlers/compositor.rs @@ -3,9 +3,12 @@ use crate::{shell::grabs::SeatMoveGrabState, state::ClientState, utils::prelude::*}; use calloop::Interest; use smithay::{ - backend::renderer::{ - element::{Kind, surface::KindEvaluation}, - utils::{on_commit_buffer_handler, with_renderer_surface_state}, + backend::{ + input::InputTime, + renderer::{ + element::{Kind, surface::KindEvaluation}, + utils::{on_commit_buffer_handler, with_renderer_surface_state}, + }, }, desktop::{LayerSurface, PopupKind, WindowSurfaceType, layer_map_for_output}, reexports::wayland_server::{Client, Resource, protocol::wl_surface::WlSurface}, @@ -325,9 +328,11 @@ impl CompositorHandler for State { } } else { std::mem::drop(shell); - seat.get_pointer() - .unwrap() - .unset_grab(self, SERIAL_COUNTER.next_serial(), 0); + seat.get_pointer().unwrap().unset_grab( + self, + SERIAL_COUNTER.next_serial(), + InputTime::now(), + ); return; } } diff --git a/src/wayland/handlers/pointer_constraints.rs b/src/wayland/handlers/pointer_constraints.rs index 9ce5f63a..e024bb40 100644 --- a/src/wayland/handlers/pointer_constraints.rs +++ b/src/wayland/handlers/pointer_constraints.rs @@ -5,7 +5,10 @@ use smithay::{ input::pointer::PointerHandle, reexports::wayland_server::protocol::wl_surface::WlSurface, utils::{Logical, Point}, - wayland::{pointer_constraints::PointerConstraintsHandler, seat::WaylandFocus}, + wayland::{ + pointer_constraints::{ConstraintRemove, PointerConstraintsHandler}, + seat::WaylandFocus, + }, }; pub use smithay::wayland::pointer_constraints::{PointerConstraintRef, with_pointer_constraint}; @@ -76,23 +79,38 @@ impl PointerConstraintsHandler for State { } } - 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(); + fn remove_constraint( + &mut self, + surface: &WlSurface, + pointer: &PointerHandle, + constraint_remove: ConstraintRemove, + ) { + match constraint_remove { + ConstraintRemove::PointerLeave(_) => { + // If the constraint was broken by the pointer forcibly leaving the surface, then it doesn't + // make much sense to warp it. + return; + } + ConstraintRemove::Destroyed(constraint) => { + let Some(seat) = self + .common + .shell + .read() + .seats + .iter() + .find(|s| s.get_pointer().as_ref() == Some(pointer)) + .cloned() + else { + return; + }; + let Some((hint_surface, hint_location)) = seat.pointer_constraint_hint() else { + return; + }; - 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); + if hint_surface == *surface { + self.apply_cursor_hint(surface, pointer, hint_location, Some(&constraint)); + seat.set_pointer_constraint_hint(None); + } } } } diff --git a/src/wayland/handlers/pointer_warp.rs b/src/wayland/handlers/pointer_warp.rs index 971ab583..634817cf 100644 --- a/src/wayland/handlers/pointer_warp.rs +++ b/src/wayland/handlers/pointer_warp.rs @@ -38,7 +38,7 @@ impl PointerWarpHandler for State { drop(shell); if let Some(pointer_handle) = pointer_handle { - self.apply_cursor_hint(&surface, &pointer_handle, pos); + self.apply_cursor_hint(&surface, &pointer_handle, pos, None); } } } diff --git a/src/wayland/handlers/toplevel_management.rs b/src/wayland/handlers/toplevel_management.rs index 3da39952..b1616bca 100644 --- a/src/wayland/handlers/toplevel_management.rs +++ b/src/wayland/handlers/toplevel_management.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only use smithay::{ + backend::input::InputTime, desktop::{WindowSurfaceType, layer_map_for_output}, input::{Seat, pointer::MotionEvent}, output::Output, @@ -124,7 +125,7 @@ impl ToplevelManagementHandler for State { &MotionEvent { location: new_pos.to_f64().as_logical(), serial, - time: self.common.clock.now().as_millis(), + time: InputTime::now(), }, ); ptr.frame(self); diff --git a/src/xwayland.rs b/src/xwayland.rs index da3fb36b..68fb95b3 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -20,7 +20,7 @@ use smithay::{ backend::{ allocator::Fourcc, drm::DrmNode, - input::{ButtonState, KeyState, Keycode}, + input::{ButtonState, InputTime, KeyState, Keycode}, renderer::{ Bind, Frame, Offscreen, Renderer, element::{ @@ -396,7 +396,7 @@ impl Common { state: KeyState, modifiers: ModifiersState, serial: Serial, - time: u32, + time: InputTime, ) { let config = self.config.cosmic_conf.xwayland_eavesdropping.keyboard; if config == EavesdroppingKeyboardMode::None { @@ -458,7 +458,7 @@ impl Common { tracing::trace!("Forwaring key {} {:?} to xwayland", code.raw() - 8, state); for wl_keyboard in keyboard.client_keyboards(&xstate.client) { - wl_keyboard.key(serial.into(), time, code.raw() - 8, state.into()); + wl_keyboard.key(serial.into(), time.millis(), code.raw() - 8, state.into()); if xstate.last_modifier_state != Some(modifiers) { xstate.last_modifier_state = Some(modifiers); wl_keyboard.modifiers( @@ -478,7 +478,7 @@ impl Common { button: u32, state: ButtonState, serial: Serial, - time: u32, + time: InputTime, ) { if !self.config.cosmic_conf.xwayland_eavesdropping.pointer { return; @@ -518,7 +518,7 @@ impl Common { tracing::trace!("Forwaring ptr button {} {:?} to Xwayland", button, state); for wl_pointer in pointer.client_pointers(&xstate.client) { - wl_pointer.button(serial.into(), time, button, state.into()); + wl_pointer.button(serial.into(), time.millis(), button, state.into()); } } From 158e2b7f91e3e6b3a569a7ea26251182a1e40627 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Tue, 25 Aug 2026 19:21:49 +0200 Subject: [PATCH 35/80] i18n: translation updates from weblate Co-authored-by: Kurt Harren Catindig --- resources/i18n/fil/cosmic_comp.ftl | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/i18n/fil/cosmic_comp.ftl diff --git a/resources/i18n/fil/cosmic_comp.ftl b/resources/i18n/fil/cosmic_comp.ftl new file mode 100644 index 00000000..e69de29b From 94178034ddc3386bf256fcc952f72ddb55997d49 Mon Sep 17 00:00:00 2001 From: RipleyTom Date: Mon, 27 Jul 2026 20:53:57 +0200 Subject: [PATCH 36/80] feat: allow XWayland window to start minimized --- src/shell/mod.rs | 13 +++++++++++-- src/wayland/handlers/xdg_shell/mod.rs | 1 + src/xwayland.rs | 3 +++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 009c98dc..3fa863d3 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -254,6 +254,7 @@ pub struct PendingWindow { pub surface: CosmicSurface, pub seat: Seat, pub fullscreen: Option, + pub minimized: bool, pub maximized: bool, pub sticky: bool, } @@ -2856,6 +2857,7 @@ impl Shell { surface: window, seat, fullscreen: output, + minimized: should_be_minimized, maximized: should_be_maximized, sticky: mut should_be_sticky, } = self.pending_windows.remove(pos); @@ -2939,6 +2941,7 @@ impl Shell { if let Some(FocusTarget::Window(focused)) = maybe_focused && let Some(stack) = focused.stack_ref() && !is_dialog + && !should_be_minimized && !should_be_maximized && !(workspace.is_tiled(&focused.active_window()) && floating_exception) { @@ -2988,8 +2991,13 @@ impl Shell { self.maximize_request(&mapped, &seat, false, loop_handle); } - let new_target = if (workspace_output == seat.active_output() - && active_handle == workspace_handle) + if should_be_minimized { + self.minimize_request(&window); + } + + let new_target = if should_be_minimized { + None + } else if (workspace_output == seat.active_output() && active_handle == workspace_handle) || should_be_sticky { // TODO: enforce focus stealing prevention by also checking the same rules as for the else case. @@ -3113,6 +3121,7 @@ impl Shell { surface, seat: seat.clone(), fullscreen: None, + minimized: false, maximized: false, sticky: false, }); diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index b4f16c4c..c9fb926f 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -50,6 +50,7 @@ impl XdgShellHandler for State { surface, seat, fullscreen: None, + minimized: false, maximized: false, sticky: false, }) diff --git a/src/xwayland.rs b/src/xwayland.rs index 68fb95b3..78193d13 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -808,6 +808,7 @@ impl XwmHandler for State { ); } let fullscreen = window.is_fullscreen().then(|| seat.active_output()); + let minimized = window.is_hidden(); let maximized = window.is_maximized(); if let Some(pending) = shell .pending_windows @@ -816,6 +817,7 @@ impl XwmHandler for State { { pending.seat = seat; pending.fullscreen = fullscreen; + pending.minimized = minimized; pending.maximized = maximized; } else { let surface = CosmicSurface::from(window); @@ -823,6 +825,7 @@ impl XwmHandler for State { surface, seat, fullscreen, + minimized, maximized, sticky: false, }) From 9fe22c86b600ca13ac2d31fa4d5eb924b3443227 Mon Sep 17 00:00:00 2001 From: Hojjat Date: Thu, 27 Aug 2026 15:24:25 -0600 Subject: [PATCH 37/80] fix(corner-radius): use layer surface size for radius validation --- src/wayland/protocols/corner_radius.rs | 34 ++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/wayland/protocols/corner_radius.rs b/src/wayland/protocols/corner_radius.rs index 51dc1153..479f890a 100644 --- a/src/wayland/protocols/corner_radius.rs +++ b/src/wayland/protocols/corner_radius.rs @@ -5,16 +5,15 @@ use cosmic_protocols::corner_radius::v1::server::cosmic_corner_radius_toplevel_v use cosmic_protocols::corner_radius::v1::server::{ cosmic_corner_radius_manager_v1, cosmic_corner_radius_toplevel_v1, }; -use smithay::desktop::utils::bbox_from_surface_tree; use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_popup::XdgPopup; use smithay::reexports::wayland_protocols_wlr::layer_shell::v1::server::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1; use smithay::reexports::wayland_server::New; use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; -use smithay::utils::{HookId, Logical, Point, Rectangle}; +use smithay::utils::{HookId, Logical, Rectangle}; use smithay::wayland::compositor::Cacheable; use smithay::wayland::compositor::add_pre_commit_hook; use smithay::wayland::compositor::with_states; -use smithay::wayland::shell::wlr_layer::WlrLayerShellHandler; +use smithay::wayland::shell::wlr_layer::{LayerSurfaceAttributes, WlrLayerShellHandler}; use smithay::wayland::shell::xdg::{SurfaceCachedState, XdgShellSurfaceUserData}; use smithay::{ reexports::{ @@ -655,7 +654,6 @@ fn pad_rect( } fn layer_radius_hook(_state: &mut D, _dh: &DisplayHandle, surface: &WlSurface) { - let bbox = bbox_from_surface_tree(surface, Point::default()); with_states(surface, |surface_data| { let corners = *surface_data .cached_state @@ -665,8 +663,34 @@ fn layer_radius_hook(_state: &mut D, _dh: &DisplayHandle, surface: & .cached_state .get::() .pending(); + + // The radius and padding is relative to the size of `zwlr_layer_surface_v1` (according to + // the protocol). That is the configure the client acked. + // + // `last_acked` is the becomes current as part of this commit, + // so it is staged consistently with the pending radius/padding above. + // + // Until the client acks a configure there is no defined size, and nothing to check. + let Some(size) = surface_data + .data_map + .get::>() + .and_then(|attrs| { + attrs + .lock() + .unwrap() + .last_acked + .as_ref() + .and_then(|configure| configure.state.size) + }) + else { + return; + }; + let empty = Padding::default(); - let Some(padded_box) = pad_rect(bbox, padding.0.as_ref().unwrap_or(&empty)) else { + let Some(padded_box) = pad_rect( + Rectangle::from_size(size), + padding.0.as_ref().unwrap_or(&empty), + ) else { if let Some(hook) = surface_data.data_map.get::() { let hook_ref = hook.lock().unwrap(); if let Some((_, obj)) = hook_ref.as_ref() From b407ed560a4ac179d0289d231095ff20739fb919 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 14 Aug 2026 18:25:41 +0200 Subject: [PATCH 38/80] wayland/decoration: Make default decoration mode configurable --- cosmic-comp-config/src/lib.rs | 9 +++ src/config/mod.rs | 11 ++- src/wayland/handlers/decoration.rs | 108 +++++++++++++++++++++++++++-- 3 files changed, 120 insertions(+), 8 deletions(-) diff --git a/cosmic-comp-config/src/lib.rs b/cosmic-comp-config/src/lib.rs index 853ba447..510927f9 100644 --- a/cosmic-comp-config/src/lib.rs +++ b/cosmic-comp-config/src/lib.rs @@ -66,6 +66,13 @@ impl Default for AppearanceConfig { } } +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum DecorationPreference { + #[default] + ClientSide, + ServerSide, +} + #[derive(Clone, Debug, PartialEq, CosmicConfigEntry)] #[version = 1] pub struct CosmicCompConfig { @@ -104,6 +111,7 @@ pub struct CosmicCompConfig { /// Briefly magnify the cursor when the pointer is shaken, to help locate it pub cursor_shake_to_find: bool, pub activation_policy: ActivationPolicy, + pub decoration_preference: DecorationPreference, } impl Default for CosmicCompConfig { @@ -143,6 +151,7 @@ impl Default for CosmicCompConfig { cursor_hide_timeout: None, cursor_shake_to_find: true, activation_policy: ActivationPolicy::default(), + decoration_preference: DecorationPreference::default(), } } } diff --git a/src/config/mod.rs b/src/config/mod.rs index abef562b..9db9a9bb 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -49,8 +49,8 @@ mod types; use cosmic::config::CosmicTk; pub use cosmic_comp_config::EdidProduct; use cosmic_comp_config::{ - ActivationPolicy, AppearanceConfig, CosmicCompConfig, KeyboardConfig, TileBehavior, XkbConfig, - XwaylandDescaling, XwaylandEavesdropping, ZoomConfig, + ActivationPolicy, AppearanceConfig, CosmicCompConfig, DecorationPreference, KeyboardConfig, + TileBehavior, XkbConfig, XwaylandDescaling, XwaylandEavesdropping, ZoomConfig, input::{DeviceState as InputDeviceState, InputConfig, TouchpadOverride}, output::comp::{ OutputConfig, OutputInfo, OutputState, OutputsConfig, TransformDef, load_outputs, @@ -1000,6 +1000,13 @@ fn config_changed(config: cosmic_config::Config, keys: Vec, state: &mut state.common.config.cosmic_conf.activation_policy = new; } } + "decoration_preference" => { + let new = get_config::(&config, "decoration_preference"); + if new != state.common.config.cosmic_conf.decoration_preference { + state.common.config.cosmic_conf.decoration_preference = new; + state.update_decorations(); + } + } _ => {} } } diff --git a/src/wayland/handlers/decoration.rs b/src/wayland/handlers/decoration.rs index 90ec5855..ae58d338 100644 --- a/src/wayland/handlers/decoration.rs +++ b/src/wayland/handlers/decoration.rs @@ -1,5 +1,6 @@ use std::{cell::RefCell, sync::Mutex}; +use cosmic_comp_config::DecorationPreference; use smithay::{ desktop::Window, reexports::{ @@ -20,7 +21,7 @@ use smithay::{ }; use wayland_backend::protocol::WEnum; -use crate::state::State; +use crate::{shell::CosmicMapped, state::State}; pub struct PreferredDecorationMode(RefCell>); @@ -29,7 +30,7 @@ impl PreferredDecorationMode { window .user_data() .get::() - .is_none() + .is_none_or(|mode| mode.0.borrow().is_none()) } pub fn mode(window: &Window) -> Option { @@ -53,6 +54,73 @@ impl PreferredDecorationMode { } } +impl State { + pub fn default_decoration(&self) -> DecorationPreference { + self.common.config.cosmic_conf.decoration_preference + } + + pub fn update_decorations(&self) { + let shell = self.common.shell.read(); + + let update = |mapped: &CosmicMapped| { + let mode = if mapped.is_stack() { + XdgMode::ServerSide + } else { + XdgMode::from_preference(self.default_decoration()) + }; + for (window, _) in mapped.windows() { + if PreferredDecorationMode::is_unset(&window.0) { + if let Some(toplevel) = window.0.toplevel() { + if toplevel.with_committed_state(|state| { + state.is_some_and(|state| state.decoration_mode.is_some()) + }) { + toplevel.with_pending_state(|state| { + state.decoration_mode = Some(mode); + }); + toplevel.send_configure(); + } + } + } + } + }; + + for set in shell.workspaces.sets.values() { + set.sticky_layer.mapped().for_each(update); + } + + for space in shell.workspaces.spaces() { + space.mapped().for_each(update); + space + .minimized_windows + .iter() + .filter_map(|m| m.mapped()) + .for_each(update); + } + } +} + +trait FromDecorationPreference { + fn from_preference(preference: DecorationPreference) -> Self; +} + +impl FromDecorationPreference for XdgMode { + fn from_preference(preference: DecorationPreference) -> Self { + match preference { + DecorationPreference::ClientSide => XdgMode::ClientSide, + DecorationPreference::ServerSide => XdgMode::ServerSide, + } + } +} + +impl FromDecorationPreference for KdeMode { + fn from_preference(preference: DecorationPreference) -> Self { + match preference { + DecorationPreference::ClientSide => KdeMode::Client, + DecorationPreference::ServerSide => KdeMode::Server, + } + } +} + pub type KdeDecorationData = Mutex; #[derive(Debug, Default)] pub struct KdeDecorationSurfaceState { @@ -67,7 +135,7 @@ impl XdgDecorationHandler for State { let mode = if mapped.is_stack() { XdgMode::ServerSide } else { - XdgMode::ClientSide + XdgMode::from_preference(self.default_decoration()) }; if let Some((window, _)) = mapped @@ -80,6 +148,10 @@ impl XdgDecorationHandler for State { }); toplevel.send_configure(); } + } else { + toplevel.with_pending_state(|state| { + state.decoration_mode = Some(XdgMode::from_preference(self.default_decoration())) + }) } } @@ -99,6 +171,13 @@ impl XdgDecorationHandler for State { } } else { toplevel.with_pending_state(|state| state.decoration_mode = Some(mode)); + if let Some(pending) = shell + .pending_windows + .iter() + .find(|pending| pending.surface.0.toplevel().is_some_and(|t| t == &toplevel)) + { + PreferredDecorationMode::update(&pending.surface.0, Some(mode)); + } } } @@ -110,11 +189,28 @@ impl XdgDecorationHandler for State { .find(|(window, _)| window.wl_surface().as_deref() == Some(toplevel.wl_surface())) && let Some(toplevel) = window.0.toplevel() { + let mode = if mapped.is_stack() { + XdgMode::ServerSide + } else { + XdgMode::from_preference(self.default_decoration()) + }; + PreferredDecorationMode::update(&window.0, None); toplevel.with_pending_state(|state| { - state.decoration_mode = None; + state.decoration_mode = Some(mode); }); toplevel.send_configure(); + } else { + toplevel.with_pending_state(|state| { + state.decoration_mode = Some(XdgMode::from_preference(self.default_decoration())) + }); + if let Some(pending) = shell + .pending_windows + .iter() + .find(|pending| pending.surface.0.toplevel().is_some_and(|t| t == &toplevel)) + { + PreferredDecorationMode::update(&pending.surface.0, None); + } } } } @@ -129,10 +225,10 @@ impl KdeDecorationHandler for State { if mapped.is_stack() { KdeMode::Server } else { - KdeMode::Client + KdeMode::from_preference(self.default_decoration()) } } else { - KdeMode::Client + KdeMode::from_preference(self.default_decoration()) }; with_states(surface, |states| { From a7d31a75d293e98fe75151c167451d5da50397a8 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 24 Jul 2026 14:14:15 +0200 Subject: [PATCH 39/80] backend/kms: Check all connectors for primary gpu --- src/backend/kms/mod.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 6da04aac..36c6407c 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -32,7 +32,11 @@ use smithay::{ calloop::{Dispatcher, EventLoop, LoopHandle}, drm::{ Device as _, - control::{Device as _, connector::Interface, crtc}, + control::{ + Device as _, + connector::{Interface, State as ConnectorState}, + crtc, + }, }, input::{self, Libinput}, wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1::TrancheFlags, @@ -250,13 +254,18 @@ fn determine_primary_gpu( // try to find builtin display for dev in drm_devices.values() { - if dev.inner.surfaces.values().any(|s| { - if let Ok(conn_info) = dev.drm.device().get_connector(s.connector, false) { - let i = conn_info.interface(); - i == Interface::EmbeddedDisplayPort || i == Interface::LVDS || i == Interface::DSI - } else { - false - } + let drm = dev.drm.device(); + let res_handles = drm.resource_handles()?; + let connectors = res_handles.connectors(); + if connectors.iter().any(|conn| { + let Ok(conn_info) = drm.get_connector(*conn, false) else { + return false; + }; + let i = conn_info.interface(); + conn_info.state() == ConnectorState::Connected + && (i == Interface::EmbeddedDisplayPort + || i == Interface::LVDS + || i == Interface::DSI) }) { return Ok(Some(dev.inner.render_node)); } From e93ec67a117126ce445abf1588a7d2f8180bf3b0 Mon Sep 17 00:00:00 2001 From: Shehriyar Qureshi Date: Tue, 1 Sep 2026 21:05:18 +0500 Subject: [PATCH 40/80] fix(kms): typo in log message --- src/backend/kms/device.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/kms/device.rs b/src/backend/kms/device.rs index 418a16f3..c58450bc 100644 --- a/src/backend/kms/device.rs +++ b/src/backend/kms/device.rs @@ -693,7 +693,7 @@ impl Device { ) .with_context(|| { format!( - "Failed to optain file descriptor for drm device: {}", + "Failed to obtain file descriptor for drm device: {}", path.display() ) })?, From 9059873c84c79c8d14f54464f2a126d8c9491ceb Mon Sep 17 00:00:00 2001 From: Konstantinos Date: Fri, 28 Aug 2026 22:22:53 +0300 Subject: [PATCH 41/80] fix: Do not magnify cursor when there is an active grab. --- src/input/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/input/mod.rs b/src/input/mod.rs index d619a525..6055ad33 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -338,6 +338,9 @@ impl State { let current_output = seat.active_output(); if self.common.config.cosmic_conf.cursor_shake_to_find + && seat + .get_pointer() + .is_some_and(|pointer| !pointer.is_grabbed()) && let Some(cursor_state) = seat.user_data() .get::() From 86919566440fffbb437aff9c000774a7c32ce6b4 Mon Sep 17 00:00:00 2001 From: Hojjat Date: Wed, 2 Sep 2026 13:45:28 -0600 Subject: [PATCH 42/80] fix: validate layer radius against the pending surface size --- src/wayland/protocols/corner_radius.rs | 49 +++++++++++++++----------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/src/wayland/protocols/corner_radius.rs b/src/wayland/protocols/corner_radius.rs index 479f890a..5c1a200c 100644 --- a/src/wayland/protocols/corner_radius.rs +++ b/src/wayland/protocols/corner_radius.rs @@ -5,16 +5,19 @@ use cosmic_protocols::corner_radius::v1::server::cosmic_corner_radius_toplevel_v use cosmic_protocols::corner_radius::v1::server::{ cosmic_corner_radius_manager_v1, cosmic_corner_radius_toplevel_v1, }; +use smithay::backend::renderer::buffer_dimensions; +use smithay::backend::renderer::utils::RendererSurfaceStateUserData; use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_popup::XdgPopup; use smithay::reexports::wayland_protocols_wlr::layer_shell::v1::server::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1; use smithay::reexports::wayland_server::New; use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; use smithay::utils::{HookId, Logical, Rectangle}; -use smithay::wayland::compositor::Cacheable; use smithay::wayland::compositor::add_pre_commit_hook; use smithay::wayland::compositor::with_states; -use smithay::wayland::shell::wlr_layer::{LayerSurfaceAttributes, WlrLayerShellHandler}; +use smithay::wayland::compositor::{BufferAssignment, Cacheable, SurfaceAttributes}; +use smithay::wayland::shell::wlr_layer::WlrLayerShellHandler; use smithay::wayland::shell::xdg::{SurfaceCachedState, XdgShellSurfaceUserData}; +use smithay::wayland::viewporter::ViewportCachedState; use smithay::{ reexports::{ wayland_protocols::xdg::shell::server::xdg_toplevel::XdgToplevel, @@ -664,25 +667,31 @@ fn layer_radius_hook(_state: &mut D, _dh: &DisplayHandle, surface: & .get::() .pending(); - // The radius and padding is relative to the size of `zwlr_layer_surface_v1` (according to - // the protocol). That is the configure the client acked. - // - // `last_acked` is the becomes current as part of this commit, - // so it is staged consistently with the pending radius/padding above. - // - // Until the client acks a configure there is no defined size, and nothing to check. - let Some(size) = surface_data - .data_map - .get::>() - .and_then(|attrs| { - attrs - .lock() - .unwrap() - .last_acked - .as_ref() - .and_then(|configure| configure.state.size) + // The wl_surface size after this commit is: + // - viewport destination if set + // - else the logical size of the buffer that will be current attached now + // - else existing + let size = surface_data + .cached_state + .get::() + .pending() + .size() + .or_else(|| { + let mut guard = surface_data.cached_state.get::(); + let attrs = guard.pending(); + match &attrs.buffer { + Some(BufferAssignment::NewBuffer(buffer)) => buffer_dimensions(buffer) + .map(|d| d.to_logical(attrs.buffer_scale, attrs.buffer_transform.into())), + _ => None, + } }) - else { + .or_else(|| { + surface_data + .data_map + .get::() + .and_then(|state| state.lock().unwrap().surface_size()) + }); + let Some(size) = size else { return; }; From e62e35e5cc6bfa5cd5ba9dee718245497667037d Mon Sep 17 00:00:00 2001 From: Frederic Laing Date: Mon, 31 Aug 2026 20:23:45 +0200 Subject: [PATCH 43/80] fix: keep Xwayland frame callbacks progressing --- src/state.rs | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/state.rs b/src/state.rs index 5ccc7a7b..76c4c098 100644 --- a/src/state.rs +++ b/src/state.rs @@ -139,6 +139,14 @@ struct Localizations; pub static LANG_LOADER: LazyLock = LazyLock::new(|| fluent_language_loader!()); +fn xwayland_frame_throttle(throttle: Option, is_xwayland: bool) -> Option { + if is_xwayland { + Some(Duration::ZERO) + } else { + throttle + } +} + #[macro_export] macro_rules! fl { ($message_id:literal) => {{ @@ -1365,7 +1373,9 @@ impl Common { && let Some(grab_state) = move_grab.lock().unwrap().as_ref() { for (window, _) in grab_state.element().windows() { - window.send_frame(output, time, throttle(&window), should_send); + let throttle = + xwayland_frame_throttle(throttle(&window), window.x11_surface().is_some()); + window.send_frame(output, time, throttle, should_send); } } @@ -1389,25 +1399,34 @@ impl Common { .mapped() .for_each(|mapped| { for (window, _) in mapped.windows() { - window.send_frame(output, time, throttle(&window), should_send); + let throttle = + xwayland_frame_throttle(throttle(&window), window.x11_surface().is_some()); + window.send_frame(output, time, throttle, should_send); } }); if let Some(active) = shell.active_space(output) { if let Some(fs) = active.get_fullscreen(shell.seats.last_active()) { - fs.surface - .send_frame(output, time, throttle(&fs.surface), should_send); + let throttle = xwayland_frame_throttle( + throttle(&fs.surface), + fs.surface.x11_surface().is_some(), + ); + fs.surface.send_frame(output, time, throttle, should_send); } active.mapped().for_each(|mapped| { for (window, _) in mapped.windows() { - window.send_frame(output, time, throttle(&window), should_send); + let throttle = + xwayland_frame_throttle(throttle(&window), window.x11_surface().is_some()); + window.send_frame(output, time, throttle, should_send); } }); // other (throttled) windows active.minimized_windows.iter().for_each(|m| { for window in m.windows() { - window.send_frame(output, time, throttle(&window), |_, _| None); + let throttle = + xwayland_frame_throttle(throttle(&window), window.x11_surface().is_some()); + window.send_frame(output, time, throttle, |_, _| None); } }); @@ -1418,17 +1437,25 @@ impl Common { { if let Some(fs) = space.get_fullscreen(shell.seats.last_active()) { let throttle = min(throttle(space), throttle(&fs.surface)); + let throttle = + xwayland_frame_throttle(throttle, fs.surface.x11_surface().is_some()); fs.surface.send_frame(output, time, throttle, |_, _| None); } space.mapped().for_each(|mapped| { for (window, _) in mapped.windows() { let throttle = min(throttle(space), throttle(&window)); + let throttle = + xwayland_frame_throttle(throttle, window.x11_surface().is_some()); window.send_frame(output, time, throttle, |_, _| None); } }); space.minimized_windows.iter().for_each(|m| { for window in m.windows() { - window.send_frame(output, time, throttle(&window), |_, _| None); + let throttle = xwayland_frame_throttle( + throttle(&window), + window.x11_surface().is_some(), + ); + window.send_frame(output, time, throttle, |_, _| None); } }) } From 0d7592857964fe1061440b2b4504dbc4d3ec826d Mon Sep 17 00:00:00 2001 From: Frederic Laing Date: Tue, 1 Sep 2026 21:04:55 +0200 Subject: [PATCH 44/80] chore: refactor throttle function --- src/state.rs | 52 ++++++++++++++++++++-------------------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/src/state.rs b/src/state.rs index 76c4c098..28ed8fd4 100644 --- a/src/state.rs +++ b/src/state.rs @@ -139,14 +139,6 @@ struct Localizations; pub static LANG_LOADER: LazyLock = LazyLock::new(|| fluent_language_loader!()); -fn xwayland_frame_throttle(throttle: Option, is_xwayland: bool) -> Option { - if is_xwayland { - Some(Duration::ZERO) - } else { - throttle - } -} - #[macro_export] macro_rules! fl { ($message_id:literal) => {{ @@ -1336,8 +1328,12 @@ impl Common { const THROTTLE: Option = Some(Duration::from_millis(995)); const SCREENCOPY_THROTTLE: Option = Some(Duration::from_nanos(16_666_666)); - fn throttle(session_holder: &impl SessionHolder) -> Option { - if session_holder.sessions().is_empty() && session_holder.cursor_sessions().is_empty() { + fn throttle(session_holder: &impl SessionHolder, is_xwayland: bool) -> Option { + if is_xwayland { + Some(Duration::ZERO) + } else if session_holder.sessions().is_empty() + && session_holder.cursor_sessions().is_empty() + { THROTTLE } else { SCREENCOPY_THROTTLE @@ -1373,8 +1369,7 @@ impl Common { && let Some(grab_state) = move_grab.lock().unwrap().as_ref() { for (window, _) in grab_state.element().windows() { - let throttle = - xwayland_frame_throttle(throttle(&window), window.x11_surface().is_some()); + let throttle = throttle(&window, window.x11_surface().is_some()); window.send_frame(output, time, throttle, should_send); } } @@ -1399,24 +1394,19 @@ impl Common { .mapped() .for_each(|mapped| { for (window, _) in mapped.windows() { - let throttle = - xwayland_frame_throttle(throttle(&window), window.x11_surface().is_some()); + let throttle = throttle(&window, window.x11_surface().is_some()); window.send_frame(output, time, throttle, should_send); } }); if let Some(active) = shell.active_space(output) { if let Some(fs) = active.get_fullscreen(shell.seats.last_active()) { - let throttle = xwayland_frame_throttle( - throttle(&fs.surface), - fs.surface.x11_surface().is_some(), - ); + let throttle = throttle(&fs.surface, fs.surface.x11_surface().is_some()); fs.surface.send_frame(output, time, throttle, should_send); } active.mapped().for_each(|mapped| { for (window, _) in mapped.windows() { - let throttle = - xwayland_frame_throttle(throttle(&window), window.x11_surface().is_some()); + let throttle = throttle(&window, window.x11_surface().is_some()); window.send_frame(output, time, throttle, should_send); } }); @@ -1424,8 +1414,7 @@ impl Common { // other (throttled) windows active.minimized_windows.iter().for_each(|m| { for window in m.windows() { - let throttle = - xwayland_frame_throttle(throttle(&window), window.x11_surface().is_some()); + let throttle = throttle(&window, window.x11_surface().is_some()); window.send_frame(output, time, throttle, |_, _| None); } }); @@ -1436,25 +1425,24 @@ impl Common { .filter(|w| w.handle != active.handle) { if let Some(fs) = space.get_fullscreen(shell.seats.last_active()) { - let throttle = min(throttle(space), throttle(&fs.surface)); - let throttle = - xwayland_frame_throttle(throttle, fs.surface.x11_surface().is_some()); + let throttle = min( + throttle(space, false), + throttle(&fs.surface, fs.surface.x11_surface().is_some()), + ); fs.surface.send_frame(output, time, throttle, |_, _| None); } space.mapped().for_each(|mapped| { for (window, _) in mapped.windows() { - let throttle = min(throttle(space), throttle(&window)); - let throttle = - xwayland_frame_throttle(throttle, window.x11_surface().is_some()); + let throttle = min( + throttle(space, false), + throttle(&window, window.x11_surface().is_some()), + ); window.send_frame(output, time, throttle, |_, _| None); } }); space.minimized_windows.iter().for_each(|m| { for window in m.windows() { - let throttle = xwayland_frame_throttle( - throttle(&window), - window.x11_surface().is_some(), - ); + let throttle = throttle(&window, window.x11_surface().is_some()); window.send_frame(output, time, throttle, |_, _| None); } }) From 8ec1ffb740dfbd02ccc6cdaa431dd3438ec51f68 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Wed, 2 Sep 2026 12:49:01 -0700 Subject: [PATCH 45/80] protocols/keyboard-layout: Send ei `modifiers` on group change This makes `cosmic-osk` show a group change on `super+space`. Previously a change like that wouldn't be sent until a keyboard input changed the modifier state, for instance releasing the shift key. --- src/config/mod.rs | 4 +++- src/input/mod.rs | 11 +++-------- src/libei.rs | 4 +++- src/wayland/handlers/keyboard_layout.rs | 6 ++++++ src/wayland/protocols/keyboard_layout.rs | 5 ++++- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 9db9a9bb..d3d2919b 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -842,7 +842,9 @@ fn config_changed(config: cosmic_config::Config, keys: Vec, state: &mut } if !state.common.ei_seats.is_empty() { let seat = state.common.shell.read().seats.last_active().clone(); - state.broadcast_ei_keyboard_modifiers(&seat); + if let Some(keyboard) = seat.get_keyboard() { + state.broadcast_ei_keyboard_modifiers(&keyboard); + } } state.common.config.cosmic_conf.xkb_config = value; } diff --git a/src/input/mod.rs b/src/input/mod.rs index 6055ad33..0228d3cb 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -49,6 +49,7 @@ use smithay::{ desktop::{PopupKeyboardGrab, WindowSurfaceType, utils::under_from_surface_tree}, input::{ Seat, + keyboard::KeyboardHandle, keyboard::{FilterResult, KeyboardSource, KeysymHandle, ModifiersState}, pointer::{ AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, @@ -1907,13 +1908,7 @@ impl State { /// Mirror the seat's current modifier state to every libei sender with a keyboard via /// `ei_keyboard.modifiers` - pub(crate) fn broadcast_ei_keyboard_modifiers(&self, seat: &Seat) { - if self.common.ei_seats.is_empty() { - return; - } - let Some(keyboard) = seat.get_keyboard() else { - return; - }; + pub(crate) fn broadcast_ei_keyboard_modifiers(&self, keyboard: &KeyboardHandle) { let s = keyboard.modifier_state().serialized; for ei_seat in self.common.ei_seats.values() { ei_seat.keyboard_modifiers(s.depressed, s.locked, s.latched, s.layout_effective); @@ -1958,7 +1953,7 @@ impl State { ) -> FilterResult> { if previous_modifiers != *modifiers { seat.set_last_modifier_change(backend_id, serial); - self.broadcast_ei_keyboard_modifiers(seat); + self.broadcast_ei_keyboard_modifiers(&seat.get_keyboard().unwrap()); } let current_focus = seat.get_keyboard().unwrap().current_focus(); diff --git a/src/libei.rs b/src/libei.rs index ad3a674d..487f0387 100644 --- a/src/libei.rs +++ b/src/libei.rs @@ -124,7 +124,9 @@ pub fn setup_ei( data.update_ei_input_method(); // Notify the remaining libei clients of the now-cleared modifier state let seat = data.common.shell.read().seats.last_active().clone(); - data.broadcast_ei_keyboard_modifiers(&seat); + if let Some(keyboard) = seat.get_keyboard() { + data.broadcast_ei_keyboard_modifiers(&keyboard); + } } EiInputEvent::Event(event) => { use smithay::backend::input::{InputEvent, KeyboardKeyEvent}; diff --git a/src/wayland/handlers/keyboard_layout.rs b/src/wayland/handlers/keyboard_layout.rs index ac1fda87..2477f93b 100644 --- a/src/wayland/handlers/keyboard_layout.rs +++ b/src/wayland/handlers/keyboard_layout.rs @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only +use smithay::input::keyboard::KeyboardHandle; + use crate::state::State; use crate::wayland::protocols::keyboard_layout::{KeyboardLayoutHandler, KeyboardLayoutState}; @@ -7,4 +9,8 @@ impl KeyboardLayoutHandler for State { fn keyboard_layout_state(&mut self) -> &mut KeyboardLayoutState { &mut self.common.keyboard_layout_state } + + fn group_changed(&mut self, keyboard: &KeyboardHandle) { + self.broadcast_ei_keyboard_modifiers(keyboard); + } } diff --git a/src/wayland/protocols/keyboard_layout.rs b/src/wayland/protocols/keyboard_layout.rs index f3a44f0a..8a8840b6 100644 --- a/src/wayland/protocols/keyboard_layout.rs +++ b/src/wayland/protocols/keyboard_layout.rs @@ -17,8 +17,10 @@ use smithay::{ use std::mem; use wayland_backend::server::{ClientId, GlobalId}; -pub trait KeyboardLayoutHandler { +pub trait KeyboardLayoutHandler: SeatHandler { fn keyboard_layout_state(&mut self) -> &mut KeyboardLayoutState; + /// Group has been changed through the protocol + fn group_changed(&mut self, keyboard: &KeyboardHandle); } #[derive(Debug)] @@ -57,6 +59,7 @@ impl KeyboardLayoutState { let active_layout = handle.with_xkb_state(state, |context| { context.xkb().lock().unwrap().active_layout() }); + state.group_changed(handle); if *last_layout != Some(active_layout) { keyboard_layout.group(active_layout.0); *last_layout = Some(active_layout); From ca9dd27350635b95532821cfb00f2e826aa22fc8 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 28 Aug 2026 14:42:17 +0200 Subject: [PATCH 46/80] chore: Fix debug feature by updating smithay-egui --- Cargo.lock | 26 ++------------------------ Cargo.toml | 1 + 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 859203e4..d0a95294 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,15 +121,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f47b57fc4521e3cae26a4d45b5227f8fadee4c345be0fefd8d5d1711afb8aeb9" -[[package]] -name = "approx" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278" -dependencies = [ - "num-traits", -] - [[package]] name = "approx" version = "0.5.1" @@ -583,16 +574,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" -[[package]] -name = "cgmath" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317" -dependencies = [ - "approx 0.4.0", - "num-traits", -] - [[package]] name = "chacha20" version = "0.10.0" @@ -3737,7 +3718,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" dependencies = [ - "approx 0.5.1", + "approx", "fast-srgb8", "palette_derive", "phf 0.11.3", @@ -4884,16 +4865,13 @@ dependencies = [ [[package]] name = "smithay-egui" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6794c95b83518f9f91ae8c2faee52302d521c0d541d016b4ed632374041d9c42" +source = "git+https://github.com/Smithay/smithay-egui.git?rev=6511552#65115521c756f356a7bcac8f1eeb715817a4666f" dependencies = [ - "cgmath", "egui", "egui_extras", "egui_glow", "image", "log", - "memoffset", "smithay", "xkbcommon 0.8.0", ] diff --git a/Cargo.toml b/Cargo.toml index 23fe2775..feb02259 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -147,3 +147,4 @@ cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch [patch.crates-io] smithay = { git = "https://github.com/smithay/smithay.git", rev = "e3d461a" } +smithay-egui = { git = "https://github.com/Smithay/smithay-egui.git", rev = "6511552" } From 6467f79025e2ffc32ed7df659c597beb4baa08b2 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 11 Aug 2026 18:18:02 +0200 Subject: [PATCH 47/80] shell: Add tablet grabs --- src/input/mod.rs | 70 ++--- src/shell/element/stack.rs | 198 +++++++++++--- src/shell/element/window.rs | 153 +++++++++-- src/shell/focus/target.rs | 132 ++++++++- src/shell/grabs/delay.rs | 161 +++++++++-- src/shell/grabs/menu/default.rs | 216 +++++++++------ src/shell/grabs/menu/mod.rs | 304 +++++++++++++++++++-- src/shell/grabs/mod.rs | 315 ++++++++++++++++++++-- src/shell/grabs/moving.rs | 152 +++++++++-- src/shell/layout/floating/grabs/resize.rs | 150 +++++++++-- src/shell/layout/tiling/grabs/resize.rs | 284 ++++++++++++++++--- src/shell/layout/tiling/mod.rs | 20 +- src/shell/mod.rs | 12 +- src/shell/zoom.rs | 217 +++++++++++++-- src/utils/iced/mod.rs | 166 +++++++++++- src/wayland/handlers/tablet_manager.rs | 6 +- src/wayland/handlers/xdg_shell/mod.rs | 39 ++- src/xwayland.rs | 58 ++-- 18 files changed, 2262 insertions(+), 391 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 0228d3cb..cbf99cae 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -52,10 +52,11 @@ use smithay::{ keyboard::KeyboardHandle, keyboard::{FilterResult, KeyboardSource, KeysymHandle, ModifiersState}, pointer::{ - AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, - GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, - GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent, - PointerGrab, PointerHandle, RelativeMotionEvent, + AxisFrame, ButtonEvent as PointerButtonEvent, GestureHoldBeginEvent, + GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, + GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, + GestureSwipeUpdateEvent, MotionEvent as PointerMotionEvent, PointerGrab, PointerHandle, + RelativeMotionEvent, }, tablet::{TabletDescriptor, TabletSeatTrait, tool}, touch::{DownEvent, MotionEvent as TouchMotionEvent, UpEvent}, @@ -326,7 +327,7 @@ impl State { } InputEvent::PointerMotion { event, .. } => { - use smithay::backend::input::PointerMotionEvent; + use smithay::backend::input::PointerMotionEvent as _; let shell = self.common.shell.write(); if let Some(seat) = shell @@ -639,7 +640,7 @@ impl State { ptr.motion( self, under, - &MotionEvent { + &PointerMotionEvent { location: position.as_logical(), serial, time: event.time(), @@ -754,7 +755,7 @@ impl State { ptr.motion( self, under, - &MotionEvent { + &PointerMotionEvent { location: position.as_logical(), serial, time: event.time(), @@ -781,9 +782,8 @@ impl State { } } InputEvent::PointerButton { event, .. } => { - use smithay::backend::input::{ButtonState, PointerButtonEvent}; + use smithay::backend::input::{ButtonState, PointerButtonEvent as _}; - // let Some(seat) = self .common .shell @@ -857,7 +857,7 @@ impl State { && !shortcuts_inhibited { let seat_clone = seat.clone(); - let mouse_button = PointerButtonEvent::button(&event); + let mouse_button = event.button(); let mut supress_button = || { // If the logo is held then the pointer event is @@ -1017,7 +1017,7 @@ impl State { if pass_event { ptr.button( self, - &ButtonEvent { + &PointerButtonEvent { button, state: event.state(), serial, @@ -1597,7 +1597,7 @@ impl State { pointer.motion( self, under.clone(), - &MotionEvent { + &PointerMotionEvent { location: position.as_logical(), serial: SERIAL_COUNTER.next_serial(), time: InputTime::now(), @@ -1626,8 +1626,7 @@ impl State { tool.motion( self, - under - .and_then(|(f, loc)| f.wl_surface().map(|s| (s.into_owned(), loc))), + under, &tool::MotionEvent { location: position.as_logical(), serial: SERIAL_COUNTER.next_serial(), @@ -1666,7 +1665,7 @@ impl State { pointer.motion( self, under.clone(), - &MotionEvent { + &PointerMotionEvent { location: position.as_logical(), serial: SERIAL_COUNTER.next_serial(), time: InputTime::now(), @@ -1698,22 +1697,17 @@ impl State { }; match event.state() { - ProximityState::In => { - let under = under.and_then(|(f, loc)| { - f.wl_surface().map(|s| (s.into_owned(), loc)) - }); - tool.proximity_in( - self, - under, - tablet, - &tool::ProximityInEvent { - location: position.as_logical(), - axis: Some(frame), - serial: SERIAL_COUNTER.next_serial(), - time: event.time(), - }, - ) - } + ProximityState::In => tool.proximity_in( + self, + under, + tablet, + &tool::ProximityInEvent { + location: position.as_logical(), + axis: Some(frame), + serial: SERIAL_COUNTER.next_serial(), + time: event.time(), + }, + ), ProximityState::Out => tool.proximity_out( self, &tool::ProximityOutEvent { @@ -1728,6 +1722,18 @@ impl State { } } InputEvent::TabletToolTip { event, .. } => { + { + let mut shell = self.common.shell.write(); + if let Some(Trigger::Tool(desc, trigger)) = + shell.overview_mode().0.active_trigger() + && event.tool() == *desc + && matches!(*trigger, tool::GrabTrigger::Tip) + && event.tip_state() == TabletToolTipState::Up + { + shell.set_overview_mode(None, self.common.event_loop_handle.clone()); + } + } + let maybe_seat = self .common .shell @@ -2968,7 +2974,7 @@ impl State { pointer.motion( self, under, - &MotionEvent { + &PointerMotionEvent { location: point.as_logical(), serial, time, diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 2fed0977..34e6f876 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -11,7 +11,7 @@ use crate::{ shell::{ element::{CosmicMappedKey, CosmicMappedKeyInner}, focus::target::PointerFocusTarget, - grabs::{ReleaseMode, ResizeEdge}, + grabs::{GrabType, ReleaseMode, ResizeEdge}, layout::tiling::NodeDesc, }, state::State, @@ -38,7 +38,7 @@ use shortcuts::action::{Direction, FocusDirection}; use smithay::{ backend::{ drm::DrmNode, - input::{InputTime, KeyState}, + input::{InputTime, KeyState, TabletToolDescriptor}, renderer::{ ImportAll, ImportMem, Renderer, element::{Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage}, @@ -52,14 +52,23 @@ use smithay::{ Seat, keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, pointer::{ - AxisFrame, ButtonEvent, CursorImageStatus, GestureHoldBeginEvent, GestureHoldEndEvent, - GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, - GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent, + AxisFrame as PointerAxisFrame, ButtonEvent as PointerButtonEvent, CursorImageStatus, + GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, + GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, + GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent as PointerMotionEvent, PointerTarget, RelativeMotionEvent, }, + tablet::{ + Tablet, TabletSeatTrait, + tool::{ + AxisFrame as ToolAxisFrame, ButtonEvent as ToolButtonEvent, + DownEvent as ToolDownEvent, MotionEvent as ToolMotionEvent, TabletToolTarget, + UpEvent as ToolUpEvent, + }, + }, touch::{ - DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, - TouchTarget, UpEvent, + DownEvent as TouchDownEvent, FrameMarker, MotionEvent as TouchMotionEvent, + OrientationEvent, ShapeEvent, TouchTarget, UpEvent as TouchUpEvent, }, }, output::Output, @@ -909,12 +918,19 @@ impl CosmicStack { false, ); if let Some((grab, focus)) = res { - if grab.is_touch_grab() { - seat.get_touch().unwrap().set_grab(state, grab, serial); - } else { - seat.get_pointer() + match grab.grab_type() { + GrabType::Touch => { + seat.get_touch().unwrap().set_grab(state, grab, serial) + } + GrabType::Pointer => seat + .get_pointer() .unwrap() - .set_grab(state, grab, serial, focus); + .set_grab(state, grab, serial, focus), + GrabType::TabletTool => seat + .tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(state, grab, InputTime::now(), serial, focus), } } }); @@ -1089,12 +1105,19 @@ impl Program for CosmicStackInternal { false, ); if let Some((grab, focus)) = res { - if grab.is_touch_grab() { - seat.get_touch().unwrap().set_grab(state, grab, serial); - } else { - seat.get_pointer() + match grab.grab_type() { + GrabType::Touch => { + seat.get_touch().unwrap().set_grab(state, grab, serial) + } + GrabType::Pointer => seat + .get_pointer() .unwrap() - .set_grab(state, grab, serial, focus); + .set_grab(state, grab, serial, focus), + GrabType::TabletTool => seat + .tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(state, grab, InputTime::now(), serial, focus), } } }); @@ -1597,7 +1620,7 @@ impl KeyboardTarget for CosmicStack { } impl PointerTarget for CosmicStack { - fn enter(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { + fn enter(&self, seat: &Seat, data: &mut State, event: &PointerMotionEvent) { let mut event = event.clone(); self.0.with_program(|p| { let active_window = &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]; @@ -1625,7 +1648,7 @@ impl PointerTarget for CosmicStack { PointerTarget::enter(&self.0, seat, data, &event) } - fn motion(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { + fn motion(&self, seat: &Seat, data: &mut State, event: &PointerMotionEvent) { let mut event = event.clone(); self.0.with_program(|p| { let active = p.active.load(Ordering::SeqCst); @@ -1668,7 +1691,7 @@ impl PointerTarget for CosmicStack { ) { } - fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) { + fn button(&self, seat: &Seat, data: &mut State, event: &PointerButtonEvent) { match self.0.with_program(|p| p.current_focus()) { Some(Focus::Header) => PointerTarget::button(&self.0, seat, data, event), Some(x) => { @@ -1700,12 +1723,19 @@ impl PointerTarget for CosmicStack { false, ); if let Some((grab, focus)) = res { - if grab.is_touch_grab() { - seat.get_touch().unwrap().set_grab(state, grab, serial); - } else { - seat.get_pointer() + match grab.grab_type() { + GrabType::Touch => { + seat.get_touch().unwrap().set_grab(state, grab, serial) + } + GrabType::Pointer => seat + .get_pointer() .unwrap() - .set_grab(state, grab, serial, focus); + .set_grab(state, grab, serial, focus), + GrabType::TabletTool => seat + .tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(state, grab, InputTime::now(), serial, focus), } } }); @@ -1714,7 +1744,7 @@ impl PointerTarget for CosmicStack { } } - fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) { + fn axis(&self, seat: &Seat, data: &mut State, frame: PointerAxisFrame) { if let Some(Focus::Header) = self.0.with_program(|p| p.current_focus()) { PointerTarget::axis(&self.0, seat, data, frame) } @@ -1763,12 +1793,19 @@ impl PointerTarget for CosmicStack { false, ); if let Some((grab, focus)) = res { - if grab.is_touch_grab() { - seat.get_touch().unwrap().set_grab(state, grab, serial); - } else { - seat.get_pointer() + match grab.grab_type() { + GrabType::Touch => { + seat.get_touch().unwrap().set_grab(state, grab, serial) + } + GrabType::Pointer => seat + .get_pointer() .unwrap() - .set_grab(state, grab, serial, focus); + .set_grab(state, grab, serial, focus), + GrabType::TabletTool => seat + .tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(state, grab, InputTime::now(), serial, focus), } } }); @@ -1842,7 +1879,7 @@ impl PointerTarget for CosmicStack { } impl TouchTarget for CosmicStack { - fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent) { + fn down(&self, seat: &Seat, data: &mut State, event: &TouchDownEvent) { let mut event = event.clone(); let active_window_geo = self.0.with_program(|p| { p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry() @@ -1853,7 +1890,7 @@ impl TouchTarget for CosmicStack { TouchTarget::down(&self.0, seat, data, &event) } - fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent) { + fn up(&self, seat: &Seat, data: &mut State, event: &TouchUpEvent) { TouchTarget::up(&self.0, seat, data, event) } @@ -1901,6 +1938,101 @@ impl TouchTarget for CosmicStack { } } +impl TabletToolTarget for CosmicStack { + fn proximity_in( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + tablet: &Tablet, + serial: Serial, + ) { + TabletToolTarget::proximity_in(&self.0, seat, data, tool_descriptor, tablet, serial) + } + + fn proximity_out( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + ) { + TabletToolTarget::proximity_out(&self.0, seat, data, tool_descriptor) + } + + fn down( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + event: &ToolDownEvent, + ) { + TabletToolTarget::down(&self.0, seat, data, tool_descriptor, event) + } + + fn up( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + event: &ToolUpEvent, + ) { + TabletToolTarget::up(&self.0, seat, data, tool_descriptor, event) + } + + fn motion( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + event: &ToolMotionEvent, + ) { + 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(); + TabletToolTarget::motion(&self.0, seat, data, tool_descriptor, &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, event.serial); + } + } + + fn axis( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + frame: ToolAxisFrame, + ) { + TabletToolTarget::axis(&self.0, seat, data, tool_descriptor, frame) + } + + fn button( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + event: &ToolButtonEvent, + ) { + TabletToolTarget::button(&self.0, seat, data, tool_descriptor, event) + } + + fn frame( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + time: InputTime, + ) { + TabletToolTarget::frame(&self.0, seat, data, tool_descriptor, time) + } +} + pub enum CosmicStackRenderElement { Header(IcedRenderElement), Shadow(PixelShaderElement), diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 53b2caee..846d9da4 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -7,7 +7,7 @@ use crate::{ shell::{ element::{CosmicMappedKey, CosmicMappedKeyInner}, focus::target::PointerFocusTarget, - grabs::{ReleaseMode, ResizeEdge}, + grabs::{GrabType, ReleaseMode, ResizeEdge}, }, state::State, utils::{ @@ -21,7 +21,7 @@ use cosmic_comp_config::AppearanceConfig; use smithay::{ backend::{ drm::DrmNode, - input::{InputTime, KeyState}, + input::{InputTime, KeyState, TabletToolDescriptor}, renderer::{ ImportAll, ImportMem, Renderer, element::{Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage}, @@ -35,14 +35,23 @@ use smithay::{ Seat, keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, pointer::{ - AxisFrame, ButtonEvent, CursorIcon, CursorImageStatus, GestureHoldBeginEvent, - GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, - GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, - GestureSwipeUpdateEvent, MotionEvent, PointerTarget, RelativeMotionEvent, + AxisFrame as PointerAxisFrame, ButtonEvent as PointerButtonEvent, CursorIcon, + CursorImageStatus, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, + GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, + GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent as PointerMotionEvent, + PointerTarget, RelativeMotionEvent, + }, + tablet::{ + Tablet, TabletSeatTrait, + tool::{ + AxisFrame as ToolAxisFrame, ButtonEvent as ToolButtonEvent, + DownEvent as ToolDownEvent, MotionEvent as ToolMotionEvent, TabletToolTarget, + UpEvent as ToolUpEvent, + }, }, touch::{ - DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, - TouchTarget, UpEvent, + DownEvent as TouchDownEvent, FrameMarker, MotionEvent as TouchMotionEvent, + OrientationEvent, ShapeEvent, TouchTarget, UpEvent as TouchUpEvent, }, }, output::Output, @@ -746,12 +755,19 @@ impl Program for CosmicWindowInternal { false, ); if let Some((grab, focus)) = res { - if grab.is_touch_grab() { - seat.get_touch().unwrap().set_grab(state, grab, serial); - } else { - seat.get_pointer() + match grab.grab_type() { + GrabType::Touch => { + seat.get_touch().unwrap().set_grab(state, grab, serial) + } + GrabType::Pointer => seat + .get_pointer() .unwrap() - .set_grab(state, grab, serial, focus); + .set_grab(state, grab, serial, focus), + GrabType::TabletTool => seat + .tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(state, grab, InputTime::now(), serial, focus), } } }); @@ -1003,7 +1019,7 @@ impl KeyboardTarget for CosmicWindow { } impl PointerTarget for CosmicWindow { - fn enter(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { + fn enter(&self, seat: &Seat, data: &mut State, event: &PointerMotionEvent) { let mut event = event.clone(); self.0.with_program(|p| { let has_ssd = p.has_ssd(false); @@ -1029,7 +1045,7 @@ impl PointerTarget for CosmicWindow { PointerTarget::enter(&self.0, seat, data, &event) } - fn motion(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { + fn motion(&self, seat: &Seat, data: &mut State, event: &PointerMotionEvent) { let mut event = event.clone(); self.0.with_program(|p| { let has_ssd = p.has_ssd(false); @@ -1061,7 +1077,7 @@ impl PointerTarget for CosmicWindow { ) { } - fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) { + fn button(&self, seat: &Seat, data: &mut State, event: &PointerButtonEvent) { match self.0.with_program(|p| p.current_focus()) { Some(Focus::Header) => PointerTarget::button(&self.0, seat, data, event), Some(x) => { @@ -1098,12 +1114,19 @@ impl PointerTarget for CosmicWindow { ); if let Some((grab, focus)) = res { - if grab.is_touch_grab() { - seat.get_touch().unwrap().set_grab(state, grab, serial); - } else { - seat.get_pointer() + match grab.grab_type() { + GrabType::Touch => { + seat.get_touch().unwrap().set_grab(state, grab, serial) + } + GrabType::Pointer => seat + .get_pointer() .unwrap() - .set_grab(state, grab, serial, focus); + .set_grab(state, grab, serial, focus), + GrabType::TabletTool => seat + .tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(state, grab, InputTime::now(), serial, focus), } } }); @@ -1112,7 +1135,7 @@ impl PointerTarget for CosmicWindow { } } - fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) { + fn axis(&self, seat: &Seat, data: &mut State, frame: PointerAxisFrame) { if let Some(Focus::Header) = self.0.with_program(|p| p.current_focus()) { PointerTarget::axis(&self.0, seat, data, frame) } @@ -1199,7 +1222,7 @@ impl PointerTarget for CosmicWindow { } impl TouchTarget for CosmicWindow { - fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent) { + fn down(&self, seat: &Seat, data: &mut State, event: &TouchDownEvent) { let mut event = event.clone(); self.0.with_program(|p| { event.location -= p.window.geometry().loc.to_f64(); @@ -1207,7 +1230,7 @@ impl TouchTarget for CosmicWindow { TouchTarget::down(&self.0, seat, data, &event) } - fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent) { + fn up(&self, seat: &Seat, data: &mut State, event: &TouchUpEvent) { TouchTarget::up(&self.0, seat, data, event) } @@ -1238,6 +1261,88 @@ impl TouchTarget for CosmicWindow { } } +impl TabletToolTarget for CosmicWindow { + fn proximity_in( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + tablet: &Tablet, + serial: Serial, + ) { + TabletToolTarget::proximity_in(&self.0, seat, data, tool_descriptor, tablet, serial) + } + + fn proximity_out( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + ) { + TabletToolTarget::proximity_out(&self.0, seat, data, tool_descriptor) + } + + fn down( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + event: &ToolDownEvent, + ) { + TabletToolTarget::down(&self.0, seat, data, tool_descriptor, event) + } + + fn up( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + event: &ToolUpEvent, + ) { + TabletToolTarget::up(&self.0, seat, data, tool_descriptor, event) + } + + fn motion( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + event: &ToolMotionEvent, + ) { + TabletToolTarget::motion(&self.0, seat, data, tool_descriptor, event) + } + + fn axis( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + frame: ToolAxisFrame, + ) { + TabletToolTarget::axis(&self.0, seat, data, tool_descriptor, frame) + } + + fn button( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + event: &ToolButtonEvent, + ) { + TabletToolTarget::button(&self.0, seat, data, tool_descriptor, event) + } + + fn frame( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + time: InputTime, + ) { + TabletToolTarget::frame(&self.0, seat, data, tool_descriptor, time) + } +} + impl WaylandFocus for CosmicWindow { fn wl_surface(&self) -> Option> { self.0.with_program(|p| { diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index 56af0ccc..6ca13279 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -16,21 +16,30 @@ use crate::{ }; use id_tree::NodeId; use smithay::{ - backend::input::{InputTime, KeyState}, + backend::input::{InputTime, KeyState, TabletToolDescriptor}, desktop::{LayerSurface, PopupKind, WindowSurface, WindowSurfaceType, space::SpaceElement}, input::{ Seat, dnd::{DndFocus, OfferData, Source}, keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, pointer::{ - AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, - GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, - GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, - MotionEvent as PointerMotionEvent, PointerTarget, RelativeMotionEvent, + AxisFrame as PointerAxisFrame, ButtonEvent as PointerButtonEvent, + GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, + GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, + GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent as PointerMotionEvent, + PointerTarget, RelativeMotionEvent, + }, + tablet::{ + Tablet, + tool::{ + AxisFrame as ToolAxisFrame, ButtonEvent as ToolButtonEvent, + DownEvent as ToolDownEvent, MotionEvent as ToolMotionEvent, TabletToolTarget, + UpEvent as ToolUpEvent, + }, }, touch::{ - DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, - TouchTarget, UpEvent, + DownEvent as TouchDownEvent, FrameMarker, MotionEvent as TouchMotionEvent, + OrientationEvent, ShapeEvent, TouchTarget, UpEvent as TouchUpEvent, }, }, reexports::wayland_server::{ @@ -164,6 +173,17 @@ impl PointerFocusTarget { } } + fn inner_tablet_tool_target(&self) -> &dyn TabletToolTarget { + match self { + PointerFocusTarget::WlSurface { surface, .. } => surface, + PointerFocusTarget::X11Surface { surface, .. } => surface, + PointerFocusTarget::StackUI(u) => u, + PointerFocusTarget::WindowUI(u) => u, + PointerFocusTarget::ResizeFork(f) => f, + PointerFocusTarget::ZoomUI(e) => e, + } + } + pub fn under_surface>>( surface: &CosmicSurface, point: P, @@ -409,10 +429,10 @@ impl PointerTarget for PointerFocusTarget { self.inner_pointer_target() .relative_motion(seat, data, event); } - fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) { + fn button(&self, seat: &Seat, data: &mut State, event: &PointerButtonEvent) { self.inner_pointer_target().button(seat, data, event); } - fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) { + fn axis(&self, seat: &Seat, data: &mut State, frame: PointerAxisFrame) { self.inner_pointer_target().axis(seat, data, frame); } fn frame(&self, seat: &Seat, data: &mut State) { @@ -498,12 +518,102 @@ impl PointerTarget for PointerFocusTarget { } } +impl TabletToolTarget for PointerFocusTarget { + fn proximity_in( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + tablet: &Tablet, + serial: Serial, + ) { + self.inner_tablet_tool_target() + .proximity_in(seat, data, tool_descriptor, tablet, serial); + } + + fn proximity_out( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + ) { + self.inner_tablet_tool_target() + .proximity_out(seat, data, tool_descriptor); + } + + fn down( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + event: &ToolDownEvent, + ) { + self.inner_tablet_tool_target() + .down(seat, data, tool_descriptor, event); + } + + fn up( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + event: &ToolUpEvent, + ) { + self.inner_tablet_tool_target() + .up(seat, data, tool_descriptor, event); + } + + fn motion( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + event: &ToolMotionEvent, + ) { + self.inner_tablet_tool_target() + .motion(seat, data, tool_descriptor, event); + } + + fn axis( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + frame: ToolAxisFrame, + ) { + self.inner_tablet_tool_target() + .axis(seat, data, tool_descriptor, frame); + } + + fn button( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + event: &ToolButtonEvent, + ) { + self.inner_tablet_tool_target() + .button(seat, data, tool_descriptor, event); + } + + fn frame( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + time: InputTime, + ) { + self.inner_tablet_tool_target() + .frame(seat, data, tool_descriptor, time); + } +} + impl TouchTarget for PointerFocusTarget { - fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent) { + fn down(&self, seat: &Seat, data: &mut State, event: &TouchDownEvent) { self.inner_touch_target().down(seat, data, event); } - fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent) { + fn up(&self, seat: &Seat, data: &mut State, event: &TouchUpEvent) { self.inner_touch_target().up(seat, data, event); } diff --git a/src/shell/grabs/delay.rs b/src/shell/grabs/delay.rs index 96d33dc6..ca19e4cb 100644 --- a/src/shell/grabs/delay.rs +++ b/src/shell/grabs/delay.rs @@ -1,22 +1,34 @@ use smithay::{ + backend::input::{InputTime, TabletToolDescriptor}, input::{ Seat, SeatHandler, pointer::{ - AxisFrame, ButtonEvent, Focus, GestureHoldBeginEvent, GestureHoldEndEvent, - GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, - GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, - GrabStartData as PointerGrabStartData, MotionEvent as PointerMotionEvent, PointerGrab, - PointerInnerHandle, RelativeMotionEvent, + AxisFrame as PointerAxisFrame, ButtonEvent as PointerButtonEvent, Focus, + GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, + GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, + GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, + MotionEvent as PointerMotionEvent, PointerGrab, PointerInnerHandle, + RelativeMotionEvent, + }, + tablet::{ + TabletSeatHandler, TabletSeatTrait, + tool::{ + AxisFrame as TabletAxisFrame, ButtonEvent as TabletButtonEvent, + DownEvent as TabletDownEvent, GrabStartData as TabletGrabStartData, + MotionEvent as TabletMotionEvent, ProximityInEvent, ProximityOutEvent, + TabletToolGrab, TabletToolInnerHandle, UpEvent as TabletUpEvent, + }, }, touch::{ - DownEvent, GrabStartData as TouchGrabStartData, MotionEvent as TouchMotionEvent, - OrientationEvent, ShapeEvent, TouchGrab, TouchInnerHandle, UpEvent, + DownEvent as TouchDownEvent, GrabStartData as TouchGrabStartData, + MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchGrab, + TouchInnerHandle, UpEvent as TouchUpEvent, }, }, utils::{Logical, Point, SERIAL_COUNTER, Serial}, }; -use crate::state::State; +use crate::{shell::grabs::GrabType, state::State}; use super::GrabStartData; @@ -44,10 +56,15 @@ impl DelayGrab { } } - pub fn is_touch_grab(&self) -> bool { - match self.start_data { - GrabStartData::Touch(_) => true, - GrabStartData::Pointer(_) => false, + pub fn grab_type(&self) -> GrabType { + self.start_data.type_() + } + + pub fn tool(&self) -> Option<&TabletToolDescriptor> { + if let GrabStartData::TabletTool { tool, .. } = &self.start_data { + Some(tool) + } else { + None } } } @@ -92,7 +109,7 @@ impl> PointerGrab for DelayGrab { &mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>, - event: &ButtonEvent, + event: &PointerButtonEvent, ) { handle.button(data, event); if handle.current_pressed().is_empty() { @@ -104,7 +121,7 @@ impl> PointerGrab for DelayGrab { &mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>, - details: AxisFrame, + details: PointerAxisFrame, ) { handle.axis(data, details); } @@ -201,12 +218,17 @@ impl> TouchGrab for DelayGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, focus: Option<(::TouchFocus, Point)>, - event: &DownEvent, + event: &TouchDownEvent, ) { handle.down(data, focus, event); } - fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) { + fn up( + &mut self, + data: &mut State, + handle: &mut TouchInnerHandle<'_, State>, + event: &TouchUpEvent, + ) { handle.up(data, event); if event.slot == TouchGrab::start_data(self).slot { @@ -273,3 +295,110 @@ impl> TouchGrab for DelayGrab { fn unset(&mut self, _data: &mut State) {} } + +impl> TabletToolGrab for DelayGrab { + fn start_data(&self) -> &TabletGrabStartData { + match &self.start_data { + GrabStartData::TabletTool { data, .. } => data, + _ => unreachable!(), + } + } + + fn proximity_out( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &ProximityOutEvent, + ) { + handle.proximity_out(data, event); + } + + fn motion( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + focus: Option<(::ToolFocus, Point)>, + event: &TabletMotionEvent, + ) { + handle.motion(data, focus, event); + + let distance = self.start_data.distance(event.location); + if distance >= 1. + && let Some(factory) = self.grab_factory.take() + { + let seat = self.seat.clone(); + let serial = self.serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()); + let time = InputTime::now(); + let Some(tool) = self.tool().cloned() else { + handle.unset_grab(self, data, serial, time, true); + return; + }; + data.common.event_loop_handle.insert_idle(move |data| { + if let Some((grab, focus)) = factory(data) { + seat.tablet_seat() + .get_tool(&tool) + .unwrap() + .set_grab(data, grab, time, serial, focus); + } + }); + } + } + + fn down( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletDownEvent, + ) { + handle.down(data, event) + } + + fn up( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletUpEvent, + ) { + handle.up(data, event); + handle.unset_grab(self, data, event.serial, event.time, false); + } + + fn button( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletButtonEvent, + ) { + handle.button(data, event); + } + + fn axis( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + frame: TabletAxisFrame, + ) { + handle.axis(data, frame) + } + + fn frame( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + time: InputTime, + ) { + handle.frame(data, time) + } + + fn unset(&mut self, _data: &mut State) {} + + fn proximity_in( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + focus: Option<(::ToolFocus, Point)>, + event: &ProximityInEvent, + ) { + handle.proximity_in(data, focus, event); + } +} diff --git a/src/shell/grabs/menu/default.rs b/src/shell/grabs/menu/default.rs index 81673efd..26cca2f5 100644 --- a/src/shell/grabs/menu/default.rs +++ b/src/shell/grabs/menu/default.rs @@ -1,7 +1,9 @@ use cosmic_settings_config::shortcuts::Action; use smithay::{ - backend::input::InputTime, input::pointer::MotionEvent, - reexports::wayland_server::protocol::wl_surface::WlSurface, utils::SERIAL_COUNTER, + backend::input::InputTime, + input::{pointer::MotionEvent, tablet::TabletSeatTrait}, + reexports::wayland_server::protocol::wl_surface::WlSurface, + utils::SERIAL_COUNTER, wayland::seat::WaylandFocus, }; @@ -11,7 +13,7 @@ use crate::{ shell::{ CosmicSurface, PointGlobalExt, Shell, element::{CosmicMapped, CosmicWindow}, - grabs::ReleaseMode, + grabs::{GrabType, ReleaseMode}, }, state::State, utils::{prelude::SeatExt, screenshot::screenshot_window}, @@ -353,19 +355,20 @@ pub fn window_items( std::mem::drop(shell); if let Some((grab, focus)) = res { - if grab.is_touch_grab() { - seat.get_touch().unwrap().set_grab( - state, - grab, - SERIAL_COUNTER.next_serial(), - ) - } else { - seat.get_pointer().unwrap().set_grab( - state, - grab, - SERIAL_COUNTER.next_serial(), - focus, - ); + let serial = SERIAL_COUNTER.next_serial(); + match grab.grab_type() { + GrabType::Touch => { + seat.get_touch().unwrap().set_grab(state, grab, serial) + } + GrabType::Pointer => seat + .get_pointer() + .unwrap() + .set_grab(state, grab, serial, focus), + GrabType::TabletTool => seat + .tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(state, grab, InputTime::now(), serial, focus), } } } @@ -389,21 +392,29 @@ pub fn window_items( std::mem::drop(shell); if let Some(((target, loc), (grab, focus))) = res { let serial = SERIAL_COUNTER.next_serial(); - if grab.is_touch_grab() { - seat.get_touch().unwrap().set_grab(state, grab, serial); - } else { - let pointer = seat.get_pointer().unwrap(); - pointer.motion( - state, - target, - &MotionEvent { - location: loc.as_logical().to_f64(), - serial, - time: InputTime::now(), - }, - ); - pointer.frame(state); - pointer.set_grab(state, grab, serial, focus); + match grab.grab_type() { + GrabType::Touch => { + seat.get_touch().unwrap().set_grab(state, grab, serial) + } + GrabType::TabletTool => seat + .tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(state, grab, InputTime::now(), serial, focus), + GrabType::Pointer => { + let pointer = seat.get_pointer().unwrap(); + pointer.motion( + state, + target, + &MotionEvent { + location: loc.as_logical().to_f64(), + serial, + time: InputTime::now(), + }, + ); + pointer.frame(state); + pointer.set_grab(state, grab, serial, focus); + } } } }); @@ -424,21 +435,30 @@ pub fn window_items( std::mem::drop(shell); if let Some(((target, loc), (grab, focus))) = res { let serial = SERIAL_COUNTER.next_serial(); - if grab.is_touch_grab() { - seat.get_touch().unwrap().set_grab(state, grab, serial); - } else { - let pointer = seat.get_pointer().unwrap(); - pointer.motion( - state, - target, - &MotionEvent { - location: loc.as_logical().to_f64(), - serial, - time: InputTime::now(), - }, - ); - pointer.frame(state); - pointer.set_grab(state, grab, serial, focus); + match grab.grab_type() { + GrabType::Touch => { + seat.get_touch().unwrap().set_grab(state, grab, serial) + } + GrabType::Pointer => { + let pointer = seat.get_pointer().unwrap(); + pointer.motion( + state, + target, + &MotionEvent { + location: loc.as_logical().to_f64(), + serial, + time: InputTime::now(), + }, + ); + pointer.frame(state); + pointer.set_grab(state, grab, serial, focus); + } + GrabType::TabletTool => { + seat.tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(state, grab, InputTime::now(), serial, focus); + } } } }); @@ -459,21 +479,30 @@ pub fn window_items( std::mem::drop(shell); if let Some(((target, loc), (grab, focus))) = res { let serial = SERIAL_COUNTER.next_serial(); - if grab.is_touch_grab() { - seat.get_touch().unwrap().set_grab(state, grab, serial); - } else { - let pointer = seat.get_pointer().unwrap(); - pointer.motion( - state, - target, - &MotionEvent { - location: loc.as_logical().to_f64(), - serial, - time: InputTime::now(), - }, - ); - pointer.frame(state); - pointer.set_grab(state, grab, serial, focus); + match grab.grab_type() { + GrabType::Touch => { + seat.get_touch().unwrap().set_grab(state, grab, serial) + } + GrabType::Pointer => { + let pointer = seat.get_pointer().unwrap(); + pointer.motion( + state, + target, + &MotionEvent { + location: loc.as_logical().to_f64(), + serial, + time: InputTime::now(), + }, + ); + pointer.frame(state); + pointer.set_grab(state, grab, serial, focus); + } + GrabType::TabletTool => { + seat.tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(state, grab, InputTime::now(), serial, focus); + } } } }); @@ -494,21 +523,29 @@ pub fn window_items( std::mem::drop(shell); if let Some(((target, loc), (grab, focus))) = res { let serial = SERIAL_COUNTER.next_serial(); - if grab.is_touch_grab() { - seat.get_touch().unwrap().set_grab(state, grab, serial); - } else { - let pointer = seat.get_pointer().unwrap(); - pointer.motion( - state, - target, - &MotionEvent { - location: loc.as_logical().to_f64(), - serial, - time: InputTime::now(), - }, - ); - pointer.frame(state); - pointer.set_grab(state, grab, serial, focus); + match grab.grab_type() { + GrabType::Touch => { + seat.get_touch().unwrap().set_grab(state, grab, serial) + } + GrabType::Pointer => { + let pointer = seat.get_pointer().unwrap(); + pointer.motion( + state, + target, + &MotionEvent { + location: loc.as_logical().to_f64(), + serial, + time: InputTime::now(), + }, + ); + pointer.frame(state); + pointer.set_grab(state, grab, serial, focus); + } + GrabType::TabletTool => seat + .tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(state, grab, InputTime::now(), serial, focus), } } }); @@ -628,19 +665,20 @@ pub fn fullscreen_items(window: &CosmicSurface, config: &Config) -> impl Iterato std::mem::drop(shell); if let Some((grab, focus)) = res { - if grab.is_touch_grab() { - seat.get_touch().unwrap().set_grab( - state, - grab, - SERIAL_COUNTER.next_serial(), - ) - } else { - seat.get_pointer().unwrap().set_grab( - state, - grab, - SERIAL_COUNTER.next_serial(), - focus, - ); + let serial = SERIAL_COUNTER.next_serial(); + match grab.grab_type() { + GrabType::Touch => { + seat.get_touch().unwrap().set_grab(state, grab, serial) + } + GrabType::Pointer => seat + .get_pointer() + .unwrap() + .set_grab(state, grab, serial, focus), + GrabType::TabletTool => seat + .tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(state, grab, InputTime::now(), serial, focus), } } } diff --git a/src/shell/grabs/menu/mod.rs b/src/shell/grabs/menu/mod.rs index 72ea065b..dafbd6cc 100644 --- a/src/shell/grabs/menu/mod.rs +++ b/src/shell/grabs/menu/mod.rs @@ -19,22 +19,33 @@ use cosmic::{ }; use smithay::{ backend::{ - input::{ButtonState, TouchSlot}, + input::{ButtonState, InputTime, TabletToolDescriptor, TouchSlot}, renderer::ImportMem, }, desktop::space::SpaceElement, input::{ Seat, pointer::{ - AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, - GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, - GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, - GrabStartData as PointerGrabStartData, MotionEvent as PointerMotionEvent, PointerGrab, - PointerInnerHandle, PointerTarget, RelativeMotionEvent, + AxisFrame as PointerAxisFrame, ButtonEvent as PointerButtonEvent, + GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, + GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, + GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, + MotionEvent as PointerMotionEvent, PointerGrab, PointerInnerHandle, PointerTarget, + RelativeMotionEvent, + }, + tablet::{ + TabletSeatHandler, + tool::{ + AxisFrame as TabletAxisFrame, ButtonEvent as TabletButtonEvent, + DownEvent as TabletDownEvent, GrabStartData as TabletGrabStartData, + MotionEvent as TabletMotionEvent, ProximityInEvent, ProximityOutEvent, + TabletToolGrab, TabletToolInnerHandle, TabletToolTarget, UpEvent as TabletUpEvent, + }, }, touch::{ - DownEvent, GrabStartData as TouchGrabStartData, MotionEvent as TouchMotionEvent, - TouchGrab, TouchInnerHandle, TouchTarget, UpEvent, + DownEvent as TouchDownEvent, GrabStartData as TouchGrabStartData, + MotionEvent as TouchMotionEvent, TouchGrab, TouchInnerHandle, TouchTarget, + UpEvent as TouchUpEvent, }, }, output::Output, @@ -43,7 +54,7 @@ use smithay::{ use crate::{ backend::render::element::AsGlowRenderer, - shell::{SeatExt, focus::target::PointerFocusTarget}, + shell::{SeatExt, focus::target::PointerFocusTarget, grabs::GrabType}, state::State, utils::{ iced::{IcedElement, IcedRenderElement, Program}, @@ -350,6 +361,7 @@ impl Program for ContextMenu { position, pointer_entered: false, touch_entered: None, + tablet_entered: None, }) } }); @@ -508,6 +520,7 @@ pub struct Element { position: Point, pointer_entered: bool, touch_entered: Option, + tablet_entered: Option, } pub struct MenuGrab { @@ -516,6 +529,7 @@ pub struct MenuGrab { seat: Seat, screen_space_relative: Option, scale: Arc>, + last_tablet_idx: Option, } impl PointerGrab for MenuGrab { @@ -598,7 +612,7 @@ impl PointerGrab for MenuGrab { &mut self, state: &mut State, handle: &mut PointerInnerHandle<'_, State>, - event: &ButtonEvent, + event: &PointerButtonEvent, ) { let any_entered = self .elements @@ -632,7 +646,7 @@ impl PointerGrab for MenuGrab { &mut self, state: &mut State, handle: &mut PointerInnerHandle<'_, State>, - details: AxisFrame, + details: PointerAxisFrame, ) { handle.axis(state, details); } @@ -729,7 +743,7 @@ impl TouchGrab for MenuGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, _focus: Option<(PointerFocusTarget, Point)>, - event: &DownEvent, + event: &TouchDownEvent, ) { { let mut guard = self.elements.lock().unwrap(); @@ -757,7 +771,7 @@ impl TouchGrab for MenuGrab { }) { let element = &mut elements[i]; - let new_event = DownEvent { + let new_event = TouchDownEvent { slot: event.slot, location: event_location - element.position.as_logical().to_f64(), serial: event.serial, @@ -772,7 +786,12 @@ impl TouchGrab for MenuGrab { handle.down(data, None, event); } - fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) { + fn up( + &mut self, + data: &mut State, + handle: &mut TouchInnerHandle<'_, State>, + event: &TouchUpEvent, + ) { { let elements = self.elements.lock().unwrap(); for element in elements.iter().filter(|elem| { @@ -848,6 +867,248 @@ impl TouchGrab for MenuGrab { fn unset(&mut self, _data: &mut State) {} } +impl TabletToolGrab for MenuGrab { + fn start_data(&self) -> &TabletGrabStartData { + match &self.start_data { + GrabStartData::TabletTool { data, .. } => data, + _ => unreachable!(), + } + } + + fn proximity_in( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + _focus: Option<(::ToolFocus, Point)>, + event: &ProximityInEvent, + ) { + { + let location = if let Some(output) = self.screen_space_relative.as_ref() { + if data.common.shell.read().zoom_state().is_some() { + event + .location + .as_global() + .to_zoomed(output) + .to_global(output) + .as_logical() + } else { + event.location + } + } else { + event.location + }; + + let mut elements = self.elements.lock().unwrap(); + if let Some(i) = elements.iter().position(|elem| { + let mut bbox = elem.iced.bbox(); + bbox.loc = elem.position.as_logical(); + + bbox.contains(location.to_i32_floor()) + }) { + let element = &mut elements[i]; + + let new_event = TabletMotionEvent { + location: location - element.position.as_logical().to_f64(), + serial: event.serial, + time: event.time, + }; + + TabletToolTarget::proximity_in( + &element.iced, + &self.seat, + data, + handle.descriptor(), + &handle.current_tablet(), + event.serial, + ); + element.tablet_entered = Some(handle.descriptor().clone()); + TabletToolTarget::motion( + &element.iced, + &self.seat, + data, + handle.descriptor(), + &new_event, + ); + self.last_tablet_idx = Some(i); + } + } + handle.proximity_in(data, None, event); + } + + fn proximity_out( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &ProximityOutEvent, + ) { + { + let mut elements = self.elements.lock().unwrap(); + for element in elements.iter_mut().filter(|elem| { + elem.tablet_entered + .as_ref() + .is_some_and(|tool| tool == handle.descriptor()) + }) { + TabletToolTarget::proximity_out( + &element.iced, + &self.seat, + data, + handle.descriptor(), + ); + element.tablet_entered.take(); + } + self.last_tablet_idx.take(); + } + handle.unset_grab(self, data, event.serial, event.time, true); + } + + fn motion( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + _focus: Option<(::ToolFocus, Point)>, + event: &TabletMotionEvent, + ) { + { + let location = if let Some(output) = self.screen_space_relative.as_ref() { + if data.common.shell.read().zoom_state().is_some() { + event + .location + .as_global() + .to_zoomed(output) + .to_global(output) + .as_logical() + } else { + event.location + } + } else { + event.location + }; + + let mut elements = self.elements.lock().unwrap(); + if let Some(i) = elements.iter().position(|elem| { + let mut bbox = elem.iced.bbox(); + bbox.loc = elem.position.as_logical(); + + bbox.contains(location.to_i32_floor()) + }) { + let element = &mut elements[i]; + + let new_event = TabletMotionEvent { + location: location - element.position.as_logical().to_f64(), + serial: event.serial, + time: event.time, + }; + + if element.tablet_entered.is_none() { + TabletToolTarget::proximity_in( + &element.iced, + &self.seat, + data, + handle.descriptor(), + &handle.current_tablet(), + event.serial, + ); + element.tablet_entered = Some(handle.descriptor().clone()); + } + TabletToolTarget::motion( + &element.iced, + &self.seat, + data, + handle.descriptor(), + &new_event, + ); + self.last_tablet_idx = Some(i); + } else { + elements + .iter_mut() + .filter(|element| element.tablet_entered.is_some()) + .skip(1) + .for_each(|element| { + TabletToolTarget::proximity_out( + &element.iced, + &self.seat, + data, + handle.descriptor(), + ); + }); + self.last_tablet_idx.take(); + } + } + handle.motion(data, None, event); + } + + fn down( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletDownEvent, + ) { + let mut guard = self.elements.lock().unwrap(); + let elements = &mut *guard; + + if let Some(elem) = self.last_tablet_idx.and_then(|i| elements.get_mut(i)) + && elem + .tablet_entered + .as_ref() + .is_some_and(|desc| desc == handle.descriptor()) + { + TabletToolTarget::down(&elem.iced, &self.seat, data, handle.descriptor(), event); + } else { + handle.down(data, event); + } + } + + fn up( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletUpEvent, + ) { + let mut guard = self.elements.lock().unwrap(); + let elements = &mut *guard; + + if let Some(elem) = self.last_tablet_idx.and_then(|i| elements.get_mut(i)) + && elem + .tablet_entered + .as_ref() + .is_some_and(|desc| desc == handle.descriptor()) + { + TabletToolTarget::up(&elem.iced, &self.seat, data, handle.descriptor(), event); + } else { + handle.up(data, event); + } + } + + fn button( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletButtonEvent, + ) { + handle.button(data, event); + } + + fn axis( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + frame: TabletAxisFrame, + ) { + handle.axis(data, frame); + } + + fn frame( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + time: InputTime, + ) { + handle.frame(data, time); + } + + fn unset(&mut self, _data: &mut State) {} +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct MenuAlignment { pub x: AxisAlignment, @@ -1051,6 +1312,7 @@ impl MenuGrab { position, pointer_entered: false, touch_entered: None, + tablet_entered: None, }])); let scale = Arc::new(Mutex::new(screen_space_relative.unwrap_or(1.))); @@ -1075,6 +1337,7 @@ impl MenuGrab { seat: seat.clone(), screen_space_relative, scale, + last_tablet_idx: None, } } @@ -1085,10 +1348,15 @@ impl MenuGrab { } } - pub fn is_touch_grab(&self) -> bool { - match self.start_data { - GrabStartData::Touch(_) => true, - GrabStartData::Pointer(_) => false, + pub fn grab_type(&self) -> GrabType { + self.start_data.type_() + } + + pub fn tool(&self) -> Option<&TabletToolDescriptor> { + if let GrabStartData::TabletTool { tool, .. } = &self.start_data { + Some(tool) + } else { + None } } } diff --git a/src/shell/grabs/mod.rs b/src/shell/grabs/mod.rs index 515916fd..9524f425 100644 --- a/src/shell/grabs/mod.rs +++ b/src/shell/grabs/mod.rs @@ -1,18 +1,26 @@ use calloop::LoopHandle; use cosmic_settings_config::shortcuts; use smithay::{ + backend::input::{InputTime, TabletToolDescriptor}, input::{ Seat, pointer::{ - AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, - GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, - GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, - GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle, - RelativeMotionEvent, + AxisFrame as PointerAxisFrame, ButtonEvent as PointerButtonEvent, + GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, + GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, + GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, + MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent, + }, + tablet::tool::{ + AxisFrame as TabletAxisFrame, ButtonEvent as TabletButtonEvent, + DownEvent as TabletDownEvent, GrabStartData as TabletGrabStartData, + MotionEvent as TabletMotionEvent, ProximityOutEvent, TabletToolGrab, + TabletToolInnerHandle, UpEvent as TabletUpEvent, }, touch::{ - DownEvent, GrabStartData as TouchGrabStartData, MotionEvent as TouchMotionEvent, - OrientationEvent, ShapeEvent, TouchGrab, TouchInnerHandle, UpEvent, + DownEvent as TouchDownEvent, GrabStartData as TouchGrabStartData, + MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchGrab, + TouchInnerHandle, UpEvent as TouchUpEvent, }, }, output::Output, @@ -39,6 +47,17 @@ use super::{ pub enum GrabStartData { Touch(TouchGrabStartData), Pointer(PointerGrabStartData), + TabletTool { + tool: TabletToolDescriptor, + data: TabletGrabStartData, + }, +} + +#[derive(Debug, Clone)] +pub enum GrabType { + Touch, + Pointer, + TabletTool, } impl GrabStartData { @@ -46,6 +65,7 @@ impl GrabStartData { match self { Self::Touch(touch) => touch.focus.as_ref(), Self::Pointer(pointer) => pointer.focus.as_ref(), + Self::TabletTool { data, .. } => data.focus.as_ref(), } } @@ -53,6 +73,7 @@ impl GrabStartData { match self { Self::Touch(touch) => touch.focus = focus, Self::Pointer(pointer) => pointer.focus = focus, + Self::TabletTool { data, .. } => data.focus = focus, } } @@ -60,6 +81,7 @@ impl GrabStartData { match self { Self::Touch(touch) => touch.location, Self::Pointer(pointer) => pointer.location, + Self::TabletTool { data, .. } => data.location, } } @@ -67,6 +89,7 @@ impl GrabStartData { match self { Self::Touch(touch) => touch.location = location, Self::Pointer(pointer) => pointer.location = location, + Self::TabletTool { data, .. } => data.location = location, } } @@ -76,6 +99,14 @@ impl GrabStartData { ((new.x - old.x).powi(2) + (new.y - old.y).powi(2)).sqrt() } + + pub fn type_(&self) -> GrabType { + match self { + Self::Pointer(_) => GrabType::Pointer, + Self::Touch(_) => GrabType::Touch, + Self::TabletTool { .. } => GrabType::TabletTool, + } + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -203,10 +234,17 @@ impl From for ResizeGrab { } impl ResizeGrab { - pub fn is_touch_grab(&self) -> bool { + pub fn grab_type(&self) -> GrabType { match self { - ResizeGrab::Floating(grab) => grab.is_touch_grab(), - ResizeGrab::Tiling(grab) => grab.is_touch_grab(), + ResizeGrab::Floating(grab) => grab.grab_type(), + ResizeGrab::Tiling(grab) => grab.grab_type(), + } + } + + pub fn tool(&self) -> Option<&TabletToolDescriptor> { + match self { + ResizeGrab::Floating(grab) => grab.tool(), + ResizeGrab::Tiling(grab) => grab.tool(), } } } @@ -242,11 +280,11 @@ impl PointerGrab for ResizeGrab { &mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>, - event: &ButtonEvent, + event: &PointerButtonEvent, ) { match self { - ResizeGrab::Floating(grab) => grab.button(data, handle, event), - ResizeGrab::Tiling(grab) => grab.button(data, handle, event), + ResizeGrab::Floating(grab) => PointerGrab::button(grab, data, handle, event), + ResizeGrab::Tiling(grab) => PointerGrab::button(grab, data, handle, event), } } @@ -254,11 +292,11 @@ impl PointerGrab for ResizeGrab { &mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>, - details: AxisFrame, + details: PointerAxisFrame, ) { match self { - ResizeGrab::Floating(grab) => grab.axis(data, handle, details), - ResizeGrab::Tiling(grab) => grab.axis(data, handle, details), + ResizeGrab::Floating(grab) => PointerGrab::axis(grab, data, handle, details), + ResizeGrab::Tiling(grab) => PointerGrab::axis(grab, data, handle, details), } } @@ -386,7 +424,7 @@ impl TouchGrab for ResizeGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, focus: Option<(PointerFocusTarget, Point)>, - event: &DownEvent, + event: &TouchDownEvent, ) { match self { ResizeGrab::Floating(grab) => TouchGrab::down(grab, data, handle, focus, event), @@ -394,7 +432,12 @@ impl TouchGrab for ResizeGrab { } } - fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) { + fn up( + &mut self, + data: &mut State, + handle: &mut TouchInnerHandle<'_, State>, + event: &TouchUpEvent, + ) { match self { ResizeGrab::Floating(grab) => TouchGrab::up(grab, data, handle, event), ResizeGrab::Tiling(grab) => TouchGrab::up(grab, data, handle, event), @@ -467,6 +510,107 @@ impl TouchGrab for ResizeGrab { } } +impl TabletToolGrab for ResizeGrab { + fn down( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletDownEvent, + ) { + match self { + ResizeGrab::Floating(grab) => TabletToolGrab::down(grab, data, handle, event), + ResizeGrab::Tiling(grab) => TabletToolGrab::down(grab, data, handle, event), + } + } + + fn up( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletUpEvent, + ) { + match self { + ResizeGrab::Floating(grab) => TabletToolGrab::up(grab, data, handle, event), + ResizeGrab::Tiling(grab) => TabletToolGrab::up(grab, data, handle, event), + } + } + + fn motion( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + focus: Option<(PointerFocusTarget, Point)>, + event: &TabletMotionEvent, + ) { + match self { + ResizeGrab::Floating(grab) => TabletToolGrab::motion(grab, data, handle, focus, event), + ResizeGrab::Tiling(grab) => TabletToolGrab::motion(grab, data, handle, focus, event), + } + } + + fn frame( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + serial: InputTime, + ) { + match self { + ResizeGrab::Floating(grab) => TabletToolGrab::frame(grab, data, handle, serial), + ResizeGrab::Tiling(grab) => TabletToolGrab::frame(grab, data, handle, serial), + } + } + + fn button( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletButtonEvent, + ) { + match self { + ResizeGrab::Floating(grab) => TabletToolGrab::button(grab, data, handle, event), + ResizeGrab::Tiling(grab) => TabletToolGrab::button(grab, data, handle, event), + } + } + + fn axis( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + details: TabletAxisFrame, + ) { + match self { + ResizeGrab::Floating(grab) => TabletToolGrab::axis(grab, data, handle, details), + ResizeGrab::Tiling(grab) => TabletToolGrab::axis(grab, data, handle, details), + } + } + + fn proximity_out( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &ProximityOutEvent, + ) { + match self { + ResizeGrab::Floating(grab) => TabletToolGrab::proximity_out(grab, data, handle, event), + ResizeGrab::Tiling(grab) => TabletToolGrab::proximity_out(grab, data, handle, event), + } + } + + fn start_data(&self) -> &TabletGrabStartData { + match self { + ResizeGrab::Floating(grab) => TabletToolGrab::start_data(grab), + ResizeGrab::Tiling(grab) => TabletToolGrab::start_data(grab), + } + } + + fn unset(&mut self, data: &mut State) { + match self { + ResizeGrab::Floating(grab) => TabletToolGrab::unset(grab, data), + ResizeGrab::Tiling(grab) => TabletToolGrab::unset(grab, data), + } + } +} + pub enum MoveGrab { Move(moving::MoveGrab), Delayed(delay::DelayGrab), @@ -536,10 +680,17 @@ impl MoveGrab { } } - pub fn is_touch_grab(&self) -> bool { + pub fn grab_type(&self) -> GrabType { match self { - MoveGrab::Move(m) => m.is_touch_grab(), - MoveGrab::Delayed(d) => d.is_touch_grab(), + MoveGrab::Move(m) => m.grab_type(), + MoveGrab::Delayed(d) => d.grab_type(), + } + } + + pub fn tool(&self) -> Option<&TabletToolDescriptor> { + match self { + MoveGrab::Move(m) => m.tool(), + MoveGrab::Delayed(d) => d.tool(), } } } @@ -574,11 +725,11 @@ impl PointerGrab for MoveGrab { &mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>, - event: &ButtonEvent, + event: &PointerButtonEvent, ) { match self { - MoveGrab::Move(grab) => grab.button(data, handle, event), - MoveGrab::Delayed(grab) => grab.button(data, handle, event), + MoveGrab::Move(grab) => PointerGrab::button(grab, data, handle, event), + MoveGrab::Delayed(grab) => PointerGrab::button(grab, data, handle, event), } } @@ -586,11 +737,11 @@ impl PointerGrab for MoveGrab { &mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>, - details: AxisFrame, + details: PointerAxisFrame, ) { match self { - MoveGrab::Move(grab) => grab.axis(data, handle, details), - MoveGrab::Delayed(grab) => grab.axis(data, handle, details), + MoveGrab::Move(grab) => PointerGrab::axis(grab, data, handle, details), + MoveGrab::Delayed(grab) => PointerGrab::axis(grab, data, handle, details), } } @@ -718,7 +869,7 @@ impl TouchGrab for MoveGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, focus: Option<(PointerFocusTarget, Point)>, - event: &DownEvent, + event: &TouchDownEvent, ) { match self { MoveGrab::Move(grab) => TouchGrab::down(grab, data, handle, focus, event), @@ -726,7 +877,12 @@ impl TouchGrab for MoveGrab { } } - fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) { + fn up( + &mut self, + data: &mut State, + handle: &mut TouchInnerHandle<'_, State>, + event: &TouchUpEvent, + ) { match self { MoveGrab::Move(grab) => TouchGrab::up(grab, data, handle, event), MoveGrab::Delayed(grab) => TouchGrab::up(grab, data, handle, event), @@ -798,3 +954,104 @@ impl TouchGrab for MoveGrab { } } } + +impl TabletToolGrab for MoveGrab { + fn down( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletDownEvent, + ) { + match self { + MoveGrab::Move(grab) => TabletToolGrab::down(grab, data, handle, event), + MoveGrab::Delayed(grab) => TabletToolGrab::down(grab, data, handle, event), + } + } + + fn up( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletUpEvent, + ) { + match self { + MoveGrab::Move(grab) => TabletToolGrab::up(grab, data, handle, event), + MoveGrab::Delayed(grab) => TabletToolGrab::up(grab, data, handle, event), + } + } + + fn motion( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + focus: Option<(PointerFocusTarget, Point)>, + event: &TabletMotionEvent, + ) { + match self { + MoveGrab::Move(grab) => TabletToolGrab::motion(grab, data, handle, focus, event), + MoveGrab::Delayed(grab) => TabletToolGrab::motion(grab, data, handle, focus, event), + } + } + + fn frame( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + serial: InputTime, + ) { + match self { + MoveGrab::Move(grab) => TabletToolGrab::frame(grab, data, handle, serial), + MoveGrab::Delayed(grab) => TabletToolGrab::frame(grab, data, handle, serial), + } + } + + fn button( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletButtonEvent, + ) { + match self { + MoveGrab::Move(grab) => TabletToolGrab::button(grab, data, handle, event), + MoveGrab::Delayed(grab) => TabletToolGrab::button(grab, data, handle, event), + } + } + + fn axis( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + details: TabletAxisFrame, + ) { + match self { + MoveGrab::Move(grab) => TabletToolGrab::axis(grab, data, handle, details), + MoveGrab::Delayed(grab) => TabletToolGrab::axis(grab, data, handle, details), + } + } + + fn proximity_out( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &ProximityOutEvent, + ) { + match self { + MoveGrab::Move(grab) => TabletToolGrab::proximity_out(grab, data, handle, event), + MoveGrab::Delayed(grab) => TabletToolGrab::proximity_out(grab, data, handle, event), + } + } + + fn start_data(&self) -> &TabletGrabStartData { + match self { + MoveGrab::Move(grab) => TabletToolGrab::start_data(grab), + MoveGrab::Delayed(grab) => TabletToolGrab::start_data(grab), + } + } + + fn unset(&mut self, data: &mut State) { + match self { + MoveGrab::Move(grab) => TabletToolGrab::unset(grab, data), + MoveGrab::Delayed(grab) => TabletToolGrab::unset(grab, data), + } + } +} diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index 05923bc7..15225212 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -8,6 +8,7 @@ use crate::{ CosmicMapped, CosmicSurface, Direction, ManagedLayer, element::{CosmicMappedRenderElement, stack_hover::StackHover}, focus::target::{KeyboardFocusTarget, PointerFocusTarget}, + grabs::GrabType, layout::floating::TiledCorners, }, utils::prelude::*, @@ -20,7 +21,7 @@ use smallvec::SmallVec; use smithay::{ backend::{ drm::DrmNode, - input::{ButtonState, InputTime}, + input::{ButtonState, InputTime, TabletToolDescriptor}, renderer::{ ImportAll, ImportMem, element::{RenderElement, utils::RescaleRenderElement}, @@ -30,13 +31,27 @@ use smithay::{ input::{ Seat, pointer::{ - AxisFrame, ButtonEvent, CursorIcon, GestureHoldBeginEvent, GestureHoldEndEvent, - GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, - GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, - GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle, + AxisFrame as PointerAxisFrame, ButtonEvent as PointerButtonEvent, CursorIcon, + GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, + GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, + GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, + MotionEvent as PointerMotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent, }, - touch::{self, GrabStartData as TouchGrabStartData, TouchGrab, TouchInnerHandle}, + tablet::{ + TabletSeatHandler, + tool::{ + AxisFrame as TabletAxisFrame, ButtonEvent as TabletButtonEvent, + DownEvent as TabletDownEvent, GrabStartData as TabletGrabStartData, + MotionEvent as TabletMotionEvent, ProximityInEvent, ProximityOutEvent, + TabletToolGrab, TabletToolInnerHandle, UpEvent as TabletUpEvent, + }, + }, + touch::{ + DownEvent as TouchDownEvent, GrabStartData as TouchGrabStartData, + MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchGrab, + TouchInnerHandle, UpEvent as TouchUpEvent, + }, }, output::Output, utils::{IsAlive, Logical, Point, Rectangle, SERIAL_COUNTER, Scale}, @@ -503,7 +518,7 @@ impl PointerGrab for MoveGrab { state: &mut State, handle: &mut PointerInnerHandle<'_, State>, _focus: Option<(PointerFocusTarget, Point)>, - event: &MotionEvent, + event: &PointerMotionEvent, ) { self.update_location(state, event.location); @@ -529,7 +544,7 @@ impl PointerGrab for MoveGrab { &mut self, state: &mut State, handle: &mut PointerInnerHandle<'_, State>, - event: &ButtonEvent, + event: &PointerButtonEvent, ) { handle.button(state, event); match self.release { @@ -550,7 +565,7 @@ impl PointerGrab for MoveGrab { &mut self, state: &mut State, handle: &mut PointerInnerHandle<'_, State>, - details: AxisFrame, + details: PointerAxisFrame, ) { handle.axis(state, details); } @@ -647,7 +662,7 @@ impl TouchGrab for MoveGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, _focus: Option<(PointerFocusTarget, Point)>, - event: &touch::DownEvent, + event: &TouchDownEvent, ) { handle.down(data, None, event) } @@ -656,7 +671,7 @@ impl TouchGrab for MoveGrab { &mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, - event: &touch::UpEvent, + event: &TouchUpEvent, ) { if event.slot == >::start_data(self).slot { handle.unset_grab(self, data); @@ -670,7 +685,7 @@ impl TouchGrab for MoveGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, _focus: Option<(PointerFocusTarget, Point)>, - event: &touch::MotionEvent, + event: &TouchMotionEvent, ) { if event.slot == >::start_data(self).slot { self.update_location(data, event.location); @@ -691,7 +706,7 @@ impl TouchGrab for MoveGrab { &mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, - event: &touch::ShapeEvent, + event: &ShapeEvent, ) { handle.shape(data, event) } @@ -700,7 +715,7 @@ impl TouchGrab for MoveGrab { &mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, - event: &touch::OrientationEvent, + event: &OrientationEvent, ) { handle.orientation(data, event) } @@ -715,6 +730,100 @@ impl TouchGrab for MoveGrab { fn unset(&mut self, _data: &mut State) {} } +impl TabletToolGrab for MoveGrab { + fn start_data(&self) -> &TabletGrabStartData { + match &self.start_data { + GrabStartData::TabletTool { data, .. } => data, + _ => unreachable!(), + } + } + + fn proximity_out( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &ProximityOutEvent, + ) { + handle.proximity_out(data, event); + } + + fn motion( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + _focus: Option<(::ToolFocus, Point)>, + event: &TabletMotionEvent, + ) { + handle.motion(data, None, event); + + self.update_location(data, event.location); + if !self.window.alive() { + handle.unset_grab(self, data, event.serial, event.time, true); + } + } + + fn down( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletDownEvent, + ) { + handle.down(data, event) + } + + fn up( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletUpEvent, + ) { + if self.tool().is_some_and(|tool| tool == handle.descriptor()) { + handle.unset_grab(self, data, event.serial, event.time, false); + } + + handle.up(data, event); + } + + fn button( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletButtonEvent, + ) { + handle.button(data, event) + } + + fn axis( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + frame: TabletAxisFrame, + ) { + handle.axis(data, frame) + } + + fn frame( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + time: InputTime, + ) { + handle.frame(data, time) + } + + fn proximity_in( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + focus: Option<(::ToolFocus, Point)>, + event: &ProximityInEvent, + ) { + handle.proximity_in(data, focus, event); + } + + fn unset(&mut self, _data: &mut State) {} +} + impl MoveGrab { pub fn new( start_data: GrabStartData, @@ -778,10 +887,15 @@ impl MoveGrab { self.previous == ManagedLayer::Tiling } - pub fn is_touch_grab(&self) -> bool { - match self.start_data { - GrabStartData::Touch(_) => true, - GrabStartData::Pointer(_) => false, + pub fn grab_type(&self) -> GrabType { + self.start_data.type_() + } + + pub fn tool(&self) -> Option<&TabletToolDescriptor> { + if let GrabStartData::TabletTool { tool, .. } = &self.start_data { + Some(tool) + } else { + None } } } @@ -953,7 +1067,7 @@ impl Drop for MoveGrab { position.as_logical().to_f64() - window.geometry().loc.to_f64() + offset, )), - &MotionEvent { + &PointerMotionEvent { location: pointer.current_location(), serial, time: InputTime::now(), diff --git a/src/shell/layout/floating/grabs/resize.rs b/src/shell/layout/floating/grabs/resize.rs index f9e5cbfd..9d767671 100644 --- a/src/shell/layout/floating/grabs/resize.rs +++ b/src/shell/layout/floating/grabs/resize.rs @@ -7,25 +7,36 @@ use crate::{ shell::{ element::CosmicMapped, focus::target::PointerFocusTarget, - grabs::{GrabStartData, ReleaseMode, ResizeEdge}, + grabs::{GrabStartData, GrabType, ReleaseMode, ResizeEdge}, }, utils::prelude::*, }; use smithay::{ - backend::input::ButtonState, + backend::input::{ButtonState, InputTime, TabletToolDescriptor}, desktop::{WindowSurface, space::SpaceElement}, input::{ Seat, pointer::{ - AxisFrame, ButtonEvent, CursorIcon, GestureHoldBeginEvent, GestureHoldEndEvent, - GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, - GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, - GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle, + AxisFrame as PointerAxisFrame, ButtonEvent as PointerButtonEvent, CursorIcon, + GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, + GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, + GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, + MotionEvent as PointerMotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent, }, + tablet::{ + TabletSeatHandler, + tool::{ + AxisFrame as TabletAxisFrame, ButtonEvent as TabletButtonEvent, + DownEvent as TabletDownEvent, GrabStartData as TabletGrabStartData, + MotionEvent as TabletMotionEvent, ProximityInEvent, ProximityOutEvent, + TabletToolGrab, TabletToolInnerHandle, UpEvent as TabletUpEvent, + }, + }, touch::{ - DownEvent, GrabStartData as TouchGrabStartData, MotionEvent as TouchMotionEvent, - OrientationEvent, ShapeEvent, TouchGrab, TouchInnerHandle, UpEvent, + DownEvent as TouchDownEvent, GrabStartData as TouchGrabStartData, + MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchGrab, + TouchInnerHandle, UpEvent as TouchUpEvent, }, }, output::Output, @@ -170,10 +181,19 @@ impl ResizeSurfaceGrab { false } - pub fn is_touch_grab(&self) -> bool { + pub fn grab_type(&self) -> GrabType { match self.start_data { - GrabStartData::Touch(_) => true, - GrabStartData::Pointer(_) => false, + GrabStartData::Pointer(_) => GrabType::Pointer, + GrabStartData::Touch(_) => GrabType::Touch, + GrabStartData::TabletTool { .. } => GrabType::TabletTool, + } + } + + pub fn tool(&self) -> Option<&TabletToolDescriptor> { + if let GrabStartData::TabletTool { tool, .. } = &self.start_data { + Some(tool) + } else { + None } } } @@ -184,7 +204,7 @@ impl PointerGrab for ResizeSurfaceGrab { data: &mut State, handle: &mut PointerInnerHandle<'_, State>, _focus: Option<(PointerFocusTarget, Point)>, - event: &MotionEvent, + event: &PointerMotionEvent, ) { // While the grab is active, no client has pointer focus handle.motion(data, None, event); @@ -209,7 +229,7 @@ impl PointerGrab for ResizeSurfaceGrab { &mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>, - event: &ButtonEvent, + event: &PointerButtonEvent, ) { handle.button(data, event); match self.release { @@ -230,7 +250,7 @@ impl PointerGrab for ResizeSurfaceGrab { &mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>, - details: AxisFrame, + details: PointerAxisFrame, ) { handle.axis(data, details) } @@ -329,12 +349,17 @@ impl TouchGrab for ResizeSurfaceGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, _focus: Option<(PointerFocusTarget, Point)>, - event: &DownEvent, + event: &TouchDownEvent, ) { handle.down(data, None, event) } - fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) { + fn up( + &mut self, + data: &mut State, + handle: &mut TouchInnerHandle<'_, State>, + event: &TouchUpEvent, + ) { if event.slot == >::start_data(self).slot { handle.unset_grab(self, data); } @@ -396,6 +421,99 @@ impl TouchGrab for ResizeSurfaceGrab { } } +impl TabletToolGrab for ResizeSurfaceGrab { + fn start_data(&self) -> &TabletGrabStartData { + match &self.start_data { + GrabStartData::TabletTool { data, .. } => data, + _ => unreachable!(), + } + } + + fn proximity_out( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &ProximityOutEvent, + ) { + handle.unset_grab(self, data, event.serial, event.time, true); + handle.proximity_out(data, event); + } + + fn motion( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + _focus: Option<(::ToolFocus, Point)>, + event: &TabletMotionEvent, + ) { + if self.update_location(event.location.as_global()) { + handle.unset_grab(self, data, event.serial, event.time, true); + } + + handle.motion(data, None, event); + } + + fn down( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletDownEvent, + ) { + handle.down(data, event) + } + + fn up( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletUpEvent, + ) { + handle.unset_grab(self, data, event.serial, event.time, true); + handle.up(data, event); + } + + fn button( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletButtonEvent, + ) { + handle.button(data, event) + } + + fn axis( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + frame: TabletAxisFrame, + ) { + handle.axis(data, frame) + } + + fn frame( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + time: InputTime, + ) { + handle.frame(data, time) + } + + fn unset(&mut self, _data: &mut State) { + self.ungrab() + } + + fn proximity_in( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + _focus: Option<(::ToolFocus, Point)>, + event: &ProximityInEvent, + ) { + handle.proximity_in(data, None, event); + } +} + pub struct ResizeGrabMarker(AtomicBool); impl ResizeGrabMarker { diff --git a/src/shell/layout/tiling/grabs/resize.rs b/src/shell/layout/tiling/grabs/resize.rs index 91873793..b6452168 100644 --- a/src/shell/layout/tiling/grabs/resize.rs +++ b/src/shell/layout/tiling/grabs/resize.rs @@ -1,34 +1,47 @@ // SPDX-License-Identifier: GPL-3.0-only +use std::cell::Cell; + use crate::{ backend::render::cursor::CursorState, shell::{ focus::target::PointerFocusTarget, - grabs::{GrabStartData, ReleaseMode}, + grabs::{GrabStartData, GrabType, ReleaseMode}, layout::Orientation, }, utils::prelude::*, }; use id_tree::{NodeId, Tree}; use smithay::{ - backend::input::{ButtonState, InputTime}, + backend::input::{ButtonState, InputTime, TabletToolDescriptor}, input::{ Seat, pointer::{ - AxisFrame, ButtonEvent, CursorIcon, Focus, GestureHoldBeginEvent, GestureHoldEndEvent, - GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, - GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, - GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle, - PointerTarget, RelativeMotionEvent, + AxisFrame as PointerAxisFrame, ButtonEvent as PointerButtonEvent, CursorIcon, Focus, + GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, + GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, + GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, + MotionEvent as PointerMotionEvent, PointerGrab, PointerInnerHandle, PointerTarget, + RelativeMotionEvent, + }, + tablet::{ + Tablet, TabletSeatHandler, TabletSeatTrait, + tool::{ + AxisFrame as TabletAxisFrame, ButtonEvent as TabletButtonEvent, + DownEvent as TabletDownEvent, GrabStartData as TabletToolGrabStartData, + GrabTrigger as TabletToolGrabTrigger, MotionEvent as TabletMotionEvent, + ProximityInEvent, ProximityOutEvent, TabletToolGrab, TabletToolInnerHandle, + TabletToolTarget, UpEvent as TabletUpEvent, + }, }, touch::{ - DownEvent, FrameMarker, GrabStartData as TouchGrabStartData, + DownEvent as TouchDownEvent, FrameMarker, GrabStartData as TouchGrabStartData, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchGrab, - TouchInnerHandle, TouchTarget, UpEvent, + TouchInnerHandle, TouchTarget, UpEvent as TouchUpEvent, }, }, output::WeakOutput, - utils::{IsAlive, Logical, Point}, + utils::{IsAlive, Logical, Point, Serial}, }; use super::super::{Data, TilingLayout}; @@ -39,6 +52,7 @@ pub struct ResizeForkTarget { pub output: WeakOutput, pub left_up_idx: usize, pub orientation: Orientation, + pub last_tablet_location: Cell>>, } impl IsAlive for ResizeForkTarget { @@ -48,7 +62,7 @@ impl IsAlive for ResizeForkTarget { } impl PointerTarget for ResizeForkTarget { - fn enter(&self, seat: &Seat, _data: &mut State, _event: &MotionEvent) { + fn enter(&self, seat: &Seat, _data: &mut State, _event: &PointerMotionEvent) { let user_data = seat.user_data(); let cursor_state = user_data.get::().unwrap(); cursor_state @@ -60,19 +74,13 @@ impl PointerTarget for ResizeForkTarget { }); } - fn leave( - &self, - seat: &Seat, - _data: &mut State, - _serial: smithay::utils::Serial, - _time: InputTime, - ) { + fn leave(&self, seat: &Seat, _data: &mut State, _serial: Serial, _time: InputTime) { let user_data = seat.user_data(); let cursor_state = user_data.get::().unwrap(); cursor_state.lock().unwrap().unset_shape(); } - fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) { + fn button(&self, seat: &Seat, data: &mut State, event: &PointerButtonEvent) { if event.button == 0x110 && event.state == ButtonState::Pressed { let seat = seat.clone(); let node = self.node.clone(); @@ -106,7 +114,7 @@ impl PointerTarget for ResizeForkTarget { } } - fn motion(&self, _seat: &Seat, _data: &mut State, _event: &MotionEvent) {} + fn motion(&self, _seat: &Seat, _data: &mut State, _event: &PointerMotionEvent) {} fn relative_motion( &self, _seat: &Seat, @@ -114,7 +122,7 @@ impl PointerTarget for ResizeForkTarget { _event: &RelativeMotionEvent, ) { } - fn axis(&self, _seat: &Seat, _data: &mut State, _frame: AxisFrame) {} + fn axis(&self, _seat: &Seat, _data: &mut State, _frame: PointerAxisFrame) {} fn frame(&self, _seat: &Seat, _data: &mut State) {} fn gesture_swipe_begin(&self, _: &Seat, _: &mut State, _: &GestureSwipeBeginEvent) {} fn gesture_swipe_update(&self, _: &Seat, _: &mut State, _: &GestureSwipeUpdateEvent) {} @@ -127,7 +135,7 @@ impl PointerTarget for ResizeForkTarget { } impl TouchTarget for ResizeForkTarget { - fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent) { + fn down(&self, seat: &Seat, data: &mut State, event: &TouchDownEvent) { let seat = seat.clone(); let node = self.node.clone(); let output = self.output.clone(); @@ -158,7 +166,7 @@ impl TouchTarget for ResizeForkTarget { }); } - fn up(&self, _seat: &Seat, _data: &mut State, _event: &UpEvent) {} + fn up(&self, _seat: &Seat, _data: &mut State, _event: &TouchUpEvent) {} 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) {} @@ -169,6 +177,113 @@ impl TouchTarget for ResizeForkTarget { } } +impl TabletToolTarget for ResizeForkTarget { + fn down( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + event: &TabletDownEvent, + ) { + let seat = seat.clone(); + let node = self.node.clone(); + let output = self.output.clone(); + let left_up_idx = self.left_up_idx; + let orientation = self.orientation; + let serial = event.serial; + let time = event.time; + let tool = tool_descriptor.clone(); + let Some(location) = self.last_tablet_location.take() else { + return; + }; + data.common.event_loop_handle.insert_idle(move |state| { + let tablet = seat.tablet_seat().get_tool(&tool).unwrap(); + tablet.set_grab( + state, + ResizeForkGrab::new( + GrabStartData::TabletTool { + tool, + data: TabletToolGrabStartData { + focus: None, + location: location.as_logical(), + trigger: TabletToolGrabTrigger::Tip, + }, + }, + location, + node, + left_up_idx, + orientation, + output, + ReleaseMode::NoMouseButtons, + ), + time, + serial, + Focus::Keep, + ) + }); + } + fn motion( + &self, + _seat: &Seat, + _data: &mut State, + _tool_descriptor: &TabletToolDescriptor, + event: &TabletMotionEvent, + ) { + self.last_tablet_location + .set(Some(event.location.as_global())) + } + fn proximity_out( + &self, + _seat: &Seat, + _data: &mut State, + _tool_descriptor: &TabletToolDescriptor, + ) { + self.last_tablet_location.set(None); + } + + fn proximity_in( + &self, + _seat: &Seat, + _data: &mut State, + _tool_descriptor: &TabletToolDescriptor, + _tablet: &Tablet, + _serial: Serial, + ) { + } + fn up( + &self, + _seat: &Seat, + _data: &mut State, + _tool_descriptor: &TabletToolDescriptor, + _event: &TabletUpEvent, + ) { + } + fn axis( + &self, + _seat: &Seat, + _data: &mut State, + _tool_descriptor: &TabletToolDescriptor, + _frame: TabletAxisFrame, + ) { + } + fn button( + &self, + _seat: &Seat, + _data: &mut State, + _tool_descriptor: &TabletToolDescriptor, + _event: &TabletButtonEvent, + ) { + } + fn frame( + &self, + _seat: &Seat, + _data: &mut State, + _tool_descriptor: &TabletToolDescriptor, + _time: InputTime, + ) { + } +} + pub struct ResizeForkGrab { start_data: GrabStartData, last_loc: Point, @@ -338,10 +453,19 @@ impl ResizeForkGrab { false } - pub fn is_touch_grab(&self) -> bool { + pub fn grab_type(&self) -> GrabType { match self.start_data { - GrabStartData::Touch(_) => true, - GrabStartData::Pointer(_) => false, + GrabStartData::Pointer(_) => GrabType::Pointer, + GrabStartData::Touch(_) => GrabType::Touch, + GrabStartData::TabletTool { .. } => GrabType::TabletTool, + } + } + + pub fn tool(&self) -> Option<&TabletToolDescriptor> { + if let GrabStartData::TabletTool { tool, .. } = &self.start_data { + Some(tool) + } else { + None } } } @@ -352,7 +476,7 @@ impl PointerGrab for ResizeForkGrab { data: &mut State, handle: &mut PointerInnerHandle<'_, State>, _focus: Option<(PointerFocusTarget, Point)>, - event: &MotionEvent, + event: &PointerMotionEvent, ) { // While the grab is active, no client has pointer focus handle.motion(data, None, event); @@ -377,7 +501,7 @@ impl PointerGrab for ResizeForkGrab { &mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>, - event: &ButtonEvent, + event: &PointerButtonEvent, ) { handle.button(data, event); match self.release { @@ -398,7 +522,7 @@ impl PointerGrab for ResizeForkGrab { &mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>, - details: AxisFrame, + details: PointerAxisFrame, ) { handle.axis(data, details) } @@ -497,12 +621,17 @@ impl TouchGrab for ResizeForkGrab { data: &mut State, handle: &mut TouchInnerHandle<'_, State>, _focus: Option<(PointerFocusTarget, Point)>, - event: &DownEvent, + event: &TouchDownEvent, ) { handle.down(data, None, event) } - fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) { + fn up( + &mut self, + data: &mut State, + handle: &mut TouchInnerHandle<'_, State>, + event: &TouchUpEvent, + ) { if event.slot == >::start_data(self).slot { handle.unset_grab(self, data); } @@ -563,3 +692,96 @@ impl TouchGrab for ResizeForkGrab { self.update_location(data, self.last_loc.as_logical(), true); } } + +impl TabletToolGrab for ResizeForkGrab { + fn start_data(&self) -> &TabletToolGrabStartData { + match &self.start_data { + GrabStartData::TabletTool { data, .. } => data, + _ => unreachable!(), + } + } + + fn proximity_out( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &ProximityOutEvent, + ) { + handle.proximity_out(data, event); + handle.unset_grab(self, data, event.serial, event.time, true); + } + + fn motion( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + _focus: Option<(::ToolFocus, Point)>, + event: &TabletMotionEvent, + ) { + handle.motion(data, None, event); + + if self.update_location(data, event.location, false) { + handle.unset_grab(self, data, event.serial, event.time, true); + } + } + + fn down( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletDownEvent, + ) { + handle.down(data, event); + } + + fn up( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletUpEvent, + ) { + handle.up(data, event); + handle.unset_grab(self, data, event.serial, event.time, true); + } + + fn button( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &TabletButtonEvent, + ) { + handle.button(data, event) + } + + fn axis( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + frame: TabletAxisFrame, + ) { + handle.axis(data, frame) + } + + fn frame( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + time: InputTime, + ) { + handle.frame(data, time) + } + + fn unset(&mut self, data: &mut State) { + self.update_location(data, self.last_loc.as_logical(), true); + } + + fn proximity_in( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + focus: Option<(::ToolFocus, Point)>, + event: &ProximityInEvent, + ) { + handle.proximity_in(data, focus, event); + } +} diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 2d95cd27..2f694acd 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -67,6 +67,7 @@ use smithay::{ wayland::{compositor::add_blocker, seat::WaylandFocus}, }; use std::{ + cell::Cell, collections::{HashMap, VecDeque}, sync::{Arc, Weak}, time::{Duration, Instant}, @@ -3348,6 +3349,7 @@ impl TilingLayout { output: self.output.downgrade(), left_up_idx: idx, orientation, + last_tablet_location: Cell::new(None), } .into(), (last_geometry.loc @@ -3381,7 +3383,7 @@ impl TilingLayout { let Some(root) = tree.root_node_id() else { if matches!( overview.active_trigger(), - Some(Trigger::Pointer(_) | Trigger::Touch(_)) + Some(Trigger::Pointer(_) | Trigger::Touch(_) | Trigger::Tool(_, _)) ) && location_f64.is_some() { let mut tree = tree.copy_clone(); @@ -3414,7 +3416,7 @@ impl TilingLayout { if matches!( overview.active_trigger(), - Some(Trigger::Pointer(_) | Trigger::Touch(_)) + Some(Trigger::Pointer(_) | Trigger::Touch(_) | Trigger::Tool(_, _)) ) { let non_exclusive_zone = layer_map_for_output(&self.output) .non_exclusive_zone() @@ -4062,8 +4064,11 @@ impl TilingLayout { let draw_groups = overview.0.alpha(); let is_overview = !matches!(overview.0, OverviewMode::None); - let is_mouse_tiling = (matches!(overview.0.trigger(), Some(Trigger::Pointer(_)))) - .then(|| self.last_overview_hover.as_ref().map(|(_, zone)| zone)); + let is_mouse_tiling = (matches!( + overview.0.trigger(), + Some(Trigger::Pointer(_) | Trigger::Tool(_, _)) + )) + .then(|| self.last_overview_hover.as_ref().map(|(_, zone)| zone)); let swap_desc = if let Some(Trigger::KeyboardSwap(_, desc)) = overview.0.trigger() { Some(desc.clone()) } else { @@ -4216,8 +4221,11 @@ impl TilingLayout { }; let draw_groups = overview.0.alpha(); - let is_mouse_tiling = (matches!(overview.0.trigger(), Some(Trigger::Pointer(_)))) - .then(|| self.last_overview_hover.as_ref().map(|(_, zone)| zone)); + let is_mouse_tiling = (matches!( + overview.0.trigger(), + Some(Trigger::Pointer(_) | Trigger::Tool(_, _)) + )) + .then(|| self.last_overview_hover.as_ref().map(|(_, zone)| zone)); let swap_desc = if let Some(Trigger::KeyboardSwap(_, desc)) = overview.0.trigger() { Some(desc.clone()) } else { diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 3fa863d3..b10d9d63 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -32,7 +32,10 @@ use cosmic_settings_config::shortcuts::action::{Direction, FocusDirection, Resiz use cosmic_settings_config::{shortcuts, window_rules::ApplicationException}; use keyframe::{ease, functions::EaseInOutCubic}; use smithay::{ - backend::{input::TouchSlot, renderer::element::RenderElementStates}, + backend::{ + input::{TabletToolDescriptor, TouchSlot}, + renderer::element::RenderElementStates, + }, desktop::{ LayerSurface, PopupKind, WindowSurface, WindowSurfaceType, layer_map_for_output, space::SpaceElement, @@ -46,6 +49,7 @@ use smithay::{ pointer::{ CursorImageStatus, CursorImageSurfaceData, Focus, GrabStartData as PointerGrabStartData, }, + tablet::tool::GrabTrigger as TabletGrabTrigger, }, output::{Output, WeakOutput}, reexports::{ @@ -130,6 +134,7 @@ pub enum Trigger { KeyboardMove(shortcuts::Modifiers), Pointer(u32), Touch(TouchSlot), + Tool(TabletToolDescriptor, TabletGrabTrigger), } #[derive(Debug, Clone)] @@ -1755,7 +1760,7 @@ impl Shell { if let Some(set) = self.workspaces.sets.get_mut(output) { if matches!( self.overview_mode.active_trigger(), - Some(Trigger::Pointer(_) | Trigger::Touch(_)) + Some(Trigger::Pointer(_) | Trigger::Touch(_) | Trigger::Tool(_, _)) ) { set.workspaces[set.active].tiling_layer.cleanup_drag(); } @@ -1806,7 +1811,7 @@ impl Shell { if let Some(set) = self.workspaces.sets.get_mut(output) { if matches!( self.overview_mode.active_trigger(), - Some(Trigger::Pointer(_) | Trigger::Touch(_)) + Some(Trigger::Pointer(_) | Trigger::Touch(_) | Trigger::Tool(_, _)) ) { set.workspaces[set.active].tiling_layer.cleanup_drag(); } @@ -3840,6 +3845,7 @@ impl Shell { let trigger = match &start_data { GrabStartData::Pointer(start_data) => Trigger::Pointer(start_data.button), GrabStartData::Touch(start_data) => Trigger::Touch(start_data.slot), + GrabStartData::TabletTool { tool, data } => Trigger::Tool(tool.clone(), data.trigger), }; let active_hint = if config.cosmic_conf.active_hint { self.theme.cosmic().active_hint as u8 diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index cfe84f06..b3a795e7 100644 --- a/src/shell/zoom.rs +++ b/src/shell/zoom.rs @@ -11,19 +11,31 @@ use cosmic_comp_config::{ZoomConfig, ZoomMovement}; use cosmic_config::ConfigSet; use keyframe::{ease, functions::Linear}; use smithay::{ - backend::{input::InputTime, renderer::ImportMem}, + backend::{ + input::{InputTime, TabletToolDescriptor}, + renderer::ImportMem, + }, desktop::space::SpaceElement, input::{ Seat, pointer::{ - AxisFrame, ButtonEvent, Focus, GestureHoldBeginEvent, GestureHoldEndEvent, - GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, - GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, - MotionEvent as PointerMotionEvent, PointerTarget, RelativeMotionEvent, + AxisFrame as PointerAxisFrame, ButtonEvent as PointerButtonEvent, Focus, + GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, + GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, + GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent as PointerMotionEvent, + PointerTarget, RelativeMotionEvent, + }, + tablet::{ + Tablet, TabletSeatTrait, + tool::{ + AxisFrame as TabletAxisFrame, ButtonEvent as TabletButtonEvent, + DownEvent as TabletDownEvent, MotionEvent as TabletMotionEvent, TabletToolTarget, + UpEvent as TabletUpEvent, + }, }, touch::{ - DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, - TouchTarget, UpEvent, + DownEvent as TouchDownEvent, FrameMarker, MotionEvent as TouchMotionEvent, + OrientationEvent, ShapeEvent, TouchTarget, UpEvent as TouchUpEvent, }, }, output::Output, @@ -33,6 +45,7 @@ use tracing::error; use crate::{ backend::render::element::AsGlowRenderer, + shell::grabs::GrabType, state::State, utils::{ iced::{IcedElement, IcedRenderElement, Program}, @@ -698,15 +711,27 @@ impl Program for ZoomProgram { ); std::mem::drop(shell); - if grab.is_touch_grab() { - seat.get_touch().unwrap().set_grab(state, grab, serial); - } else { - seat.get_pointer().unwrap().set_grab( + match grab.grab_type() { + GrabType::Touch => { + seat.get_touch().unwrap().set_grab(state, grab, serial) + } + GrabType::Pointer => seat.get_pointer().unwrap().set_grab( state, grab, serial, Focus::Clear, - ); + ), + GrabType::TabletTool => seat + .tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab( + state, + grab, + InputTime::now(), + serial, + Focus::Clear, + ), } } } @@ -785,15 +810,27 @@ impl Program for ZoomProgram { ); std::mem::drop(shell); - if grab.is_touch_grab() { - seat.get_touch().unwrap().set_grab(state, grab, serial); - } else { - seat.get_pointer().unwrap().set_grab( + match grab.grab_type() { + GrabType::Touch => { + seat.get_touch().unwrap().set_grab(state, grab, serial) + } + GrabType::Pointer => seat.get_pointer().unwrap().set_grab( state, grab, serial, Focus::Clear, - ); + ), + GrabType::TabletTool => seat + .tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab( + state, + grab, + InputTime::now(), + serial, + Focus::Clear, + ), } } } @@ -883,14 +920,14 @@ impl PointerTarget for ZoomFocusTarget { } } - fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) { + fn button(&self, seat: &Seat, data: &mut State, event: &PointerButtonEvent) { match self { ZoomFocusTarget::Main(elem) => PointerTarget::button(elem, seat, data, event), ZoomFocusTarget::Menu(elem) => PointerTarget::button(elem, seat, data, event), } } - fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) { + fn axis(&self, seat: &Seat, data: &mut State, frame: PointerAxisFrame) { match self { ZoomFocusTarget::Main(elem) => PointerTarget::axis(elem, seat, data, frame), ZoomFocusTarget::Menu(elem) => PointerTarget::axis(elem, seat, data, frame), @@ -1032,14 +1069,14 @@ impl PointerTarget for ZoomFocusTarget { } impl TouchTarget for ZoomFocusTarget { - fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent) { + fn down(&self, seat: &Seat, data: &mut State, event: &TouchDownEvent) { match self { 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) { + fn up(&self, seat: &Seat, data: &mut State, event: &TouchUpEvent) { match self { ZoomFocusTarget::Main(elem) => TouchTarget::up(elem, seat, data, event), ZoomFocusTarget::Menu(elem) => TouchTarget::up(elem, seat, data, event), @@ -1089,6 +1126,144 @@ impl TouchTarget for ZoomFocusTarget { } } +impl TabletToolTarget for ZoomFocusTarget { + fn proximity_in( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + tablet: &Tablet, + serial: Serial, + ) { + match self { + ZoomFocusTarget::Main(elem) => { + TabletToolTarget::proximity_in(elem, seat, data, tool_descriptor, tablet, serial) + } + ZoomFocusTarget::Menu(elem) => { + TabletToolTarget::proximity_in(elem, seat, data, tool_descriptor, tablet, serial) + } + } + } + + fn proximity_out( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + ) { + match self { + ZoomFocusTarget::Main(elem) => { + TabletToolTarget::proximity_out(elem, seat, data, tool_descriptor) + } + ZoomFocusTarget::Menu(elem) => { + TabletToolTarget::proximity_out(elem, seat, data, tool_descriptor) + } + } + } + + fn down( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + event: &TabletDownEvent, + ) { + match self { + ZoomFocusTarget::Main(elem) => { + TabletToolTarget::down(elem, seat, data, tool_descriptor, event) + } + ZoomFocusTarget::Menu(elem) => { + TabletToolTarget::down(elem, seat, data, tool_descriptor, event) + } + } + } + + fn up( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + event: &TabletUpEvent, + ) { + match self { + ZoomFocusTarget::Main(elem) => { + TabletToolTarget::up(elem, seat, data, tool_descriptor, event) + } + ZoomFocusTarget::Menu(elem) => { + TabletToolTarget::up(elem, seat, data, tool_descriptor, event) + } + } + } + + fn motion( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + event: &TabletMotionEvent, + ) { + match self { + ZoomFocusTarget::Main(elem) => { + TabletToolTarget::motion(elem, seat, data, tool_descriptor, event) + } + ZoomFocusTarget::Menu(elem) => { + TabletToolTarget::motion(elem, seat, data, tool_descriptor, event) + } + } + } + + fn axis( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + frame: TabletAxisFrame, + ) { + match self { + ZoomFocusTarget::Main(elem) => { + TabletToolTarget::axis(elem, seat, data, tool_descriptor, frame) + } + ZoomFocusTarget::Menu(elem) => { + TabletToolTarget::axis(elem, seat, data, tool_descriptor, frame) + } + } + } + + fn button( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + event: &TabletButtonEvent, + ) { + match self { + ZoomFocusTarget::Main(elem) => { + TabletToolTarget::button(elem, seat, data, tool_descriptor, event) + } + ZoomFocusTarget::Menu(elem) => { + TabletToolTarget::button(elem, seat, data, tool_descriptor, event) + } + } + } + + fn frame( + &self, + seat: &Seat, + data: &mut State, + tool_descriptor: &TabletToolDescriptor, + time: InputTime, + ) { + match self { + ZoomFocusTarget::Main(elem) => { + TabletToolTarget::frame(elem, seat, data, tool_descriptor, time) + } + ZoomFocusTarget::Menu(elem) => { + TabletToolTarget::frame(elem, seat, data, tool_descriptor, time) + } + } + } +} + impl IsAlive for ZoomFocusTarget { fn alive(&self) -> bool { match self { diff --git a/src/utils/iced/mod.rs b/src/utils/iced/mod.rs index 94b27ea7..8680bed3 100644 --- a/src/utils/iced/mod.rs +++ b/src/utils/iced/mod.rs @@ -31,7 +31,7 @@ use ordered_float::OrderedFloat; use smithay::{ backend::{ allocator::Fourcc, - input::{ButtonState, InputTime, KeyState}, + input::{ButtonState, InputTime, KeyState, TabletToolDescriptor}, renderer::{ ImportMem, element::{ @@ -45,14 +45,23 @@ use smithay::{ Seat, keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, pointer::{ - AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, - GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, - GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent, + AxisFrame as PointerAxisFrame, ButtonEvent as PointerButtonEvent, + GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, + GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, + GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent as PointerMotionEvent, PointerTarget, RelativeMotionEvent, }, + tablet::{ + Tablet, + tool::{ + AxisFrame as ToolAxisFrame, ButtonEvent as ToolButtonEvent, + DownEvent as ToolDownEvent, MotionEvent as ToolMotionEvent, TabletToolTarget, + UpEvent as ToolUpEvent, + }, + }, touch::{ - DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, - TouchTarget, UpEvent, + DownEvent as TouchDownEvent, FrameMarker, MotionEvent as TouchMotionEvent, + OrientationEvent, ShapeEvent, TouchTarget, UpEvent as TouchUpEvent, }, }, output::Output, @@ -189,6 +198,7 @@ pub(crate) struct IcedElementInternal { touch_map: HashMap, last_touch_frame: Option, last_touch_serial: Option, + last_tablet_serial: Option, // iced theme: Theme, @@ -240,6 +250,7 @@ impl Clone for IcedElementInternal

{ touch_map: self.touch_map.clone(), last_touch_frame: None, last_touch_serial: None, + last_tablet_serial: None, theme: self.theme.clone(), renderer, state, @@ -267,6 +278,7 @@ impl fmt::Debug for IcedElementInternal

{ .field("touch_map", &self.touch_map) .field("last_touch_frame", &self.last_touch_frame) .field("last_touch_serial", &self.last_touch_serial) + .field("last_tablet_serial", &self.last_tablet_serial) .field("theme", &"...") .field("renderer", &"...") .field("state", &"...") @@ -326,6 +338,7 @@ impl IcedElement

{ touch_map: HashMap::new(), last_touch_frame: None, last_touch_serial: None, + last_tablet_serial: None, theme, renderer, state, @@ -481,12 +494,141 @@ impl IcedElementInternal

{ } } +impl TabletToolTarget for IcedElement

{ + fn proximity_in( + &self, + _seat: &Seat, + _data: &mut crate::state::State, + _tool_descriptor: &TabletToolDescriptor, + _tablet: &Tablet, + _serial: Serial, + ) { + let mut internal = self.0.lock().unwrap(); + internal + .state + .queue_event(Event::Mouse(MouseEvent::CursorEntered)); + internal.update(false); + } + + fn proximity_out( + &self, + _seat: &Seat, + _data: &mut crate::state::State, + _tool_descriptor: &TabletToolDescriptor, + ) { + let mut internal = self.0.lock().unwrap(); + internal + .state + .queue_event(Event::Mouse(MouseEvent::CursorLeft)); + internal.update(false); + } + + fn down( + &self, + seat: &Seat, + _data: &mut crate::state::State, + _tool_descriptor: &TabletToolDescriptor, + event: &ToolDownEvent, + ) { + let mut internal = self.0.lock().unwrap(); + let id = Finger(0); + let Some(event_location) = internal.cursor_pos else { + return; + }; + let position = IcedPoint::new(event_location.x as f32, event_location.y as f32); + internal + .state + .queue_event(Event::Touch(TouchEvent::FingerPressed { id, position })); + internal.last_tablet_serial = Some(event.serial); + *internal.last_seat.lock().unwrap() = Some((seat.clone(), event.serial)); + internal.update(false); + } + + fn up( + &self, + seat: &Seat, + _data: &mut crate::state::State, + _tool_descriptor: &TabletToolDescriptor, + _event: &ToolUpEvent, + ) { + let mut internal = self.0.lock().unwrap(); + let id = Finger(0); + if let Some(event_location) = internal.cursor_pos { + *internal.last_seat.lock().unwrap() = + Some((seat.clone(), internal.last_tablet_serial.unwrap())); + let position = IcedPoint::new(event_location.x as f32, event_location.y as f32); + internal + .state + .queue_event(Event::Touch(TouchEvent::FingerLifted { id, position })); + internal.update(false); + } + } + + fn motion( + &self, + seat: &Seat, + _data: &mut crate::state::State, + _tool_descriptor: &TabletToolDescriptor, + event: &ToolMotionEvent, + ) { + let mut internal = self.0.lock().unwrap(); + let event_location = event.location.downscale(internal.additional_scale); + let position = IcedPoint::new(event_location.x as f32, event_location.y as f32); + internal + .state + .queue_event(Event::Mouse(MouseEvent::CursorMoved { position })); + internal.cursor_pos = Some(event_location); + *internal.last_seat.lock().unwrap() = Some((seat.clone(), event.serial)); + internal.update(false); + } + + fn axis( + &self, + _seat: &Seat, + _data: &mut crate::state::State, + _tool_descriptor: &TabletToolDescriptor, + _frame: ToolAxisFrame, + ) { + } + + fn button( + &self, + seat: &Seat, + _data: &mut crate::state::State, + _tool_descriptor: &TabletToolDescriptor, + event: &ToolButtonEvent, + ) { + let mut internal = self.0.lock().unwrap(); + let button = match event.button { + 0x110 => MouseButton::Left, + 0x111 => MouseButton::Right, + 0x112 => MouseButton::Middle, + x => MouseButton::Other(x as u16), + }; + internal.state.queue_event(Event::Mouse(match event.state { + ButtonState::Pressed => MouseEvent::ButtonPressed(button), + ButtonState::Released => MouseEvent::ButtonReleased(button), + })); + *internal.last_seat.lock().unwrap() = Some((seat.clone(), event.serial)); + internal.update(false); + } + + fn frame( + &self, + _seat: &Seat, + _data: &mut crate::state::State, + _tool_descriptor: &TabletToolDescriptor, + _time: InputTime, + ) { + } +} + impl PointerTarget for IcedElement

{ fn enter( &self, seat: &Seat, _data: &mut crate::state::State, - event: &MotionEvent, + event: &PointerMotionEvent, ) { let mut internal = self.0.lock().unwrap(); internal @@ -507,7 +649,7 @@ impl PointerTarget for IcedEle &self, seat: &Seat, _data: &mut crate::state::State, - event: &MotionEvent, + event: &PointerMotionEvent, ) { let mut internal = self.0.lock().unwrap(); let event_location = event.location.downscale(internal.additional_scale); @@ -532,7 +674,7 @@ impl PointerTarget for IcedEle &self, seat: &Seat, _data: &mut crate::state::State, - event: &ButtonEvent, + event: &PointerButtonEvent, ) { let mut internal = self.0.lock().unwrap(); let button = match event.button { @@ -553,7 +695,7 @@ impl PointerTarget for IcedEle &self, _seat: &Seat, _data: &mut crate::state::State, - frame: AxisFrame, + frame: PointerAxisFrame, ) { let mut internal = self.0.lock().unwrap(); internal @@ -653,7 +795,7 @@ impl TouchTarget for IcedEleme &self, seat: &Seat, _data: &mut crate::state::State, - event: &DownEvent, + event: &TouchDownEvent, ) { let mut internal = self.0.lock().unwrap(); let id = Finger(i32::from(event.slot) as u64); @@ -673,7 +815,7 @@ impl TouchTarget for IcedEleme &self, seat: &Seat, _data: &mut crate::state::State, - event: &UpEvent, + event: &TouchUpEvent, ) { let mut internal = self.0.lock().unwrap(); let id = Finger(i32::from(event.slot) as u64); diff --git a/src/wayland/handlers/tablet_manager.rs b/src/wayland/handlers/tablet_manager.rs index e4b84434..7cf2b8cb 100644 --- a/src/wayland/handlers/tablet_manager.rs +++ b/src/wayland/handlers/tablet_manager.rs @@ -1,13 +1,13 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::state::State; +use crate::{shell::focus::target::PointerFocusTarget, state::State}; use smithay::{ backend::input::TabletToolDescriptor, input::pointer::CursorImageStatus, - input::tablet::TabletSeatHandler, reexports::wayland_server::protocol::wl_surface, + input::tablet::TabletSeatHandler, }; impl TabletSeatHandler for State { - type ToolFocus = wl_surface::WlSurface; + type ToolFocus = PointerFocusTarget; fn tablet_tool_image(&mut self, _tool: &TabletToolDescriptor, _image: CursorImageStatus) { // TODO display cursor for each tablet tool diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index c9fb926f..be7084dd 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -1,11 +1,15 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - shell::{CosmicSurface, PendingWindow, focus::target::KeyboardFocusTarget, grabs::ReleaseMode}, + shell::{ + CosmicSurface, PendingWindow, + focus::target::KeyboardFocusTarget, + grabs::{GrabType, ReleaseMode}, + }, utils::prelude::*, }; -use smithay::desktop::layer_map_for_output; use smithay::{ + backend::input::InputTime, desktop::{ PopupGrab, PopupKeyboardGrab, PopupKind, PopupPointerGrab, PopupUngrabStrategy, WindowSurfaceType, find_popup_root_surface, @@ -26,6 +30,7 @@ use smithay::{ }, }, }; +use smithay::{desktop::layer_map_for_output, input::tablet::TabletSeatTrait}; use std::cell::Cell; use tracing::warn; @@ -190,12 +195,17 @@ impl XdgShellHandler for State { true, ) { std::mem::drop(shell); - if grab.is_touch_grab() { - seat.get_touch().unwrap().set_grab(self, grab, serial); - } else { - seat.get_pointer() + match grab.grab_type() { + GrabType::Touch => seat.get_touch().unwrap().set_grab(self, grab, serial), + GrabType::Pointer => seat + .get_pointer() .unwrap() - .set_grab(self, grab, serial, focus) + .set_grab(self, grab, serial, focus), + GrabType::TabletTool => seat + .tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(self, grab, InputTime::now(), serial, focus), } } } @@ -218,12 +228,17 @@ impl XdgShellHandler for State { true, ) { std::mem::drop(shell); - if grab.is_touch_grab() { - seat.get_touch().unwrap().set_grab(self, grab, serial) - } else { - seat.get_pointer() + match grab.grab_type() { + GrabType::Touch => seat.get_touch().unwrap().set_grab(self, grab, serial), + GrabType::Pointer => seat + .get_pointer() .unwrap() - .set_grab(self, grab, serial, focus) + .set_grab(self, grab, serial, focus), + GrabType::TabletTool => seat + .tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(self, grab, InputTime::now(), serial, focus), } } } diff --git a/src/xwayland.rs b/src/xwayland.rs index 78193d13..cc6be3b4 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -9,7 +9,9 @@ use std::{ use crate::{ backend::render::cursor::{Cursor, load_cursor_env, load_cursor_theme}, shell::{ - CosmicSurface, PendingWindow, Shell, focus::target::KeyboardFocusTarget, grabs::ReleaseMode, + CosmicSurface, PendingWindow, Shell, + focus::target::KeyboardFocusTarget, + grabs::{GrabType, ReleaseMode}, }, state::State, utils::prelude::*, @@ -32,7 +34,7 @@ use smithay::{ }, }, desktop::space::SpaceElement, - input::{keyboard::ModifiersState, pointer::CursorIcon}, + input::{keyboard::ModifiersState, pointer::CursorIcon, tablet::TabletSeatTrait}, reexports::{wayland_server::Client, x11rb::protocol::xproto::Window as X11Window}, utils::{ Buffer as BufferCoords, Logical, Point, Rectangle, SERIAL_COUNTER, Serial, Size, Transform, @@ -1045,17 +1047,29 @@ impl XwmHandler for State { true, ) { std::mem::drop(shell); - if grab.is_touch_grab() { - seat.get_touch() - .unwrap() - .set_grab(self, grab, SERIAL_COUNTER.next_serial()) - } else { - seat.get_pointer().unwrap().set_grab( + match grab.grab_type() { + GrabType::Touch => { + seat.get_touch() + .unwrap() + .set_grab(self, grab, SERIAL_COUNTER.next_serial()) + } + GrabType::Pointer => seat.get_pointer().unwrap().set_grab( self, grab, SERIAL_COUNTER.next_serial(), focus, - ) + ), + GrabType::TabletTool => seat + .tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab( + self, + grab, + InputTime::now(), + SERIAL_COUNTER.next_serial(), + focus, + ), } } } @@ -1076,17 +1090,29 @@ impl XwmHandler for State { true, ) { std::mem::drop(shell); - if grab.is_touch_grab() { - seat.get_touch() - .unwrap() - .set_grab(self, grab, SERIAL_COUNTER.next_serial()) - } else { - seat.get_pointer().unwrap().set_grab( + match grab.grab_type() { + GrabType::Touch => { + seat.get_touch() + .unwrap() + .set_grab(self, grab, SERIAL_COUNTER.next_serial()) + } + GrabType::Pointer => seat.get_pointer().unwrap().set_grab( self, grab, SERIAL_COUNTER.next_serial(), focus, - ) + ), + GrabType::TabletTool => seat + .tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab( + self, + grab, + InputTime::now(), + SERIAL_COUNTER.next_serial(), + focus, + ), } } } From 9125f8a2a73fc87a5697b07b71f54611e099b711 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 18 Aug 2026 19:12:06 +0200 Subject: [PATCH 48/80] input: Add pointer-emulation grab for tablet tools --- src/input/mod.rs | 100 ++++++++++++--- src/input/tablet_emu.rs | 260 ++++++++++++++++++++++++++++++++++++++ src/shell/focus/target.rs | 14 +- 3 files changed, 351 insertions(+), 23 deletions(-) create mode 100644 src/input/tablet_emu.rs diff --git a/src/input/mod.rs b/src/input/mod.rs index cbf99cae..28877356 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -9,7 +9,10 @@ use crate::{ cosmic_modifiers_from_smithay, }, }, - input::gestures::{GestureState, SwipeAction}, + input::{ + gestures::{GestureState, SwipeAction}, + tablet_emu::PointerEmulationGrab, + }, shell::{ SeatExt, Trigger, focus::{ @@ -52,7 +55,7 @@ use smithay::{ keyboard::KeyboardHandle, keyboard::{FilterResult, KeyboardSource, KeysymHandle, ModifiersState}, pointer::{ - AxisFrame, ButtonEvent as PointerButtonEvent, GestureHoldBeginEvent, + AxisFrame, ButtonEvent as PointerButtonEvent, Focus, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent as PointerMotionEvent, PointerGrab, PointerHandle, @@ -89,6 +92,7 @@ use std::{ pub mod actions; pub mod gestures; +pub mod tablet_emu; /// Identifies the input backend instance an event came from, used to disambiguate device ids /// (which are only unique within a single backend instance, see @@ -1609,6 +1613,26 @@ impl State { let tool = tablet_seat.get_tool(&event.tool()); if let Some(tool) = tool { + let serial = SERIAL_COUNTER.next_serial(); + if !tool.is_grabbed() + && under + .as_ref() + .is_some_and(|(target, _)| !target.supports_tool(&tool)) + { + let start_data = tool::GrabStartData { + focus: under.clone(), + trigger: tool::GrabTrigger::Proximity, + location: position.as_logical(), + }; + tool.set_grab( + self, + PointerEmulationGrab::new(start_data, seat.clone()), + event.time(), + serial, + Focus::Keep, + ); + } + let frame = tool::AxisFrame { pressure: event.pressure_has_changed().then(|| event.pressure()), distance: event.distance_has_changed().then(|| event.distance()), @@ -1629,7 +1653,7 @@ impl State { under, &tool::MotionEvent { location: position.as_logical(), - serial: SERIAL_COUNTER.next_serial(), + serial, time: event.time(), }, ); @@ -1683,6 +1707,25 @@ impl State { if let Some(tablet) = tablet { let serial = SERIAL_COUNTER.next_serial(); + if !tool.is_grabbed() + && under + .as_ref() + .is_some_and(|(target, _)| !target.supports_tool(&tool)) + { + let start_data = tool::GrabStartData { + focus: under.clone(), + trigger: tool::GrabTrigger::Proximity, + location: position.as_logical(), + }; + tool.set_grab( + self, + PointerEmulationGrab::new(start_data, seat.clone()), + event.time(), + serial, + Focus::Keep, + ); + } + let frame = tool::AxisFrame { pressure: event.pressure_has_changed().then(|| event.pressure()), distance: event.distance_has_changed().then(|| event.distance()), @@ -1697,24 +1740,39 @@ impl State { }; match event.state() { - ProximityState::In => tool.proximity_in( - self, - under, - tablet, - &tool::ProximityInEvent { - location: position.as_logical(), - axis: Some(frame), - serial: SERIAL_COUNTER.next_serial(), - time: event.time(), - }, - ), - ProximityState::Out => tool.proximity_out( - self, - &tool::ProximityOutEvent { - serial, - time: event.time(), - }, - ), + ProximityState::In => { + tool.proximity_in( + self, + under, + tablet, + &tool::ProximityInEvent { + location: position.as_logical(), + axis: Some(frame), + serial, + time: event.time(), + }, + ); + } + ProximityState::Out => { + tool.proximity_out( + self, + &tool::ProximityOutEvent { + serial, + time: event.time(), + }, + ); + if let Some(pointer) = seat.get_pointer() { + pointer.motion( + self, + None, + &PointerMotionEvent { + location: position.as_logical(), + serial, + time: event.time(), + }, + ); + } + } } tool.frame(self, event.time()); diff --git a/src/input/tablet_emu.rs b/src/input/tablet_emu.rs new file mode 100644 index 00000000..94d31d89 --- /dev/null +++ b/src/input/tablet_emu.rs @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-3.0-only + +//! Pointer Emulating TabletTool grab. + +use crate::{shell::focus::target::PointerFocusTarget, state::State}; +use smithay::{ + backend::input::{ButtonState, InputTime, MouseButton}, + input::{ + Seat, pointer, + tablet::{ + self, + tool::{ + AxisFrame, ButtonEvent, DownEvent, GrabStartData, ProximityInEvent, + ProximityOutEvent, TabletToolGrab, TabletToolInnerHandle, UpEvent, + }, + }, + }, + utils::{Logical, Point, SERIAL_COUNTER}, +}; + +// from https://gitlab.freedesktop.org/libinput/libinput/-/blob/main/include/linux/linux/input-event-codes.h +const BTN_STYLUS: u32 = 0x14b; +const BTN_STYLUS_2: u32 = 0x14c; +const BTN_LEFT: u32 = 0x110; +const BTN_RIGHT: u32 = 0x111; +const BTN_MIDDLE: u32 = 0x112; +const BTN_FORWARD: u32 = 0x115; +const BTN_BACK: u32 = 0x116; + +pub struct PointerEmulationGrab { + start_data: GrabStartData, + seat: Seat, + + above_source: bool, + tip_down: bool, + button_down: Vec, +} + +impl PointerEmulationGrab { + pub fn new(start_data: GrabStartData, seat: Seat) -> Self { + Self { + start_data, + seat, + + above_source: true, + tip_down: false, + button_down: Vec::new(), + } + } + + fn button_to_mouse(&self, button: u32) -> Option { + // FIXME: This should be configurable. + if button == BTN_STYLUS { + Some(MouseButton::Right) + } else if button == BTN_STYLUS_2 { + Some(MouseButton::Middle) + } else { + None + } + } + + fn pointer_button(&self, data: &mut State, button: MouseButton, state: ButtonState) { + if let Some(pointer) = self.seat.get_pointer() { + let button = match button { + MouseButton::Left => BTN_LEFT, + MouseButton::Right => BTN_RIGHT, + MouseButton::Middle => BTN_MIDDLE, + MouseButton::Back => BTN_BACK, + MouseButton::Forward => BTN_FORWARD, + _ => return, + }; + + pointer.button( + data, + &pointer::ButtonEvent { + serial: SERIAL_COUNTER.next_serial(), + button, + state, + time: InputTime::now(), + }, + ); + + pointer.frame(data); + } + } +} + +type Type = tablet::tool::MotionEvent; + +impl TabletToolGrab for PointerEmulationGrab { + fn proximity_in( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + focus: Option<(PointerFocusTarget, Point)>, + event: &ProximityInEvent, + ) { + handle.proximity_in(data, focus.clone(), event); + + if !self.above_source && !self.tip_down && self.button_down.is_empty() { + handle.unset_grab( + self, + data, + SERIAL_COUNTER.next_serial(), + InputTime::now(), + true, + ); + } else { + if let Some(pointer) = self.seat.get_pointer() { + pointer.motion( + data, + focus, + &pointer::MotionEvent { + location: event.location, + serial: SERIAL_COUNTER.next_serial(), + time: InputTime::now(), + }, + ); + pointer.frame(data); + } + } + } + + fn proximity_out( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &ProximityOutEvent, + ) { + handle.proximity_out(data, event); + + handle.unset_grab(self, data, event.serial, event.time, true); + } + + fn down( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &DownEvent, + ) { + handle.down(data, event); + self.tip_down = true; + + self.pointer_button(data, MouseButton::Left, ButtonState::Pressed); + } + + fn up( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &UpEvent, + ) { + handle.up(data, event); + + self.pointer_button(data, MouseButton::Left, ButtonState::Released); + self.tip_down = false; + + if !self.above_source && !self.tip_down && self.button_down.is_empty() { + handle.unset_grab(self, data, event.serial, event.time, true); + } + } + + fn motion( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + focus: Option<(PointerFocusTarget, Point)>, + event: &Type, + ) { + self.above_source = focus.as_ref().map(|(target, _)| target) + == self.start_data.focus.as_ref().map(|(target, _)| target); + handle.motion(data, self.start_data.focus.clone(), event); + + if !self.above_source && !self.tip_down && self.button_down.is_empty() { + handle.unset_grab( + self, + data, + SERIAL_COUNTER.next_serial(), + InputTime::now(), + true, + ); + } else { + if let Some(pointer) = self.seat.get_pointer() { + pointer.motion( + data, + focus, + &pointer::MotionEvent { + location: event.location, + serial: SERIAL_COUNTER.next_serial(), + time: InputTime::now(), + }, + ); + pointer.frame(data); + } + } + } + + fn button( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + event: &ButtonEvent, + ) { + handle.button(data, event); + + self.button_down.retain(|b| b != &event.button); + if matches!(event.state, ButtonState::Pressed) { + self.button_down.push(event.button); + } + + if let Some(button) = self.button_to_mouse(event.button) { + self.pointer_button(data, button, event.state); + } + + if !self.above_source && !self.tip_down && self.button_down.is_empty() { + handle.unset_grab( + self, + data, + SERIAL_COUNTER.next_serial(), + InputTime::now(), + true, + ); + } + } + + fn axis( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + frame: AxisFrame, + ) { + handle.axis(data, frame); + } + + fn frame( + &mut self, + data: &mut State, + handle: &mut TabletToolInnerHandle<'_, State>, + time: InputTime, + ) { + handle.frame(data, time); + } + + fn unset(&mut self, data: &mut State) { + if self.tip_down { + self.pointer_button(data, MouseButton::Left, ButtonState::Released); + } + + let buttons = std::mem::take(&mut self.button_down); + for button in buttons.into_iter() { + if let Some(button) = self.button_to_mouse(button) { + self.pointer_button(data, button, ButtonState::Released); + } + } + } + + fn start_data(&self) -> &GrabStartData { + &self.start_data + } +} diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index 6ca13279..c914e0fa 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -33,8 +33,8 @@ use smithay::{ Tablet, tool::{ AxisFrame as ToolAxisFrame, ButtonEvent as ToolButtonEvent, - DownEvent as ToolDownEvent, MotionEvent as ToolMotionEvent, TabletToolTarget, - UpEvent as ToolUpEvent, + DownEvent as ToolDownEvent, MotionEvent as ToolMotionEvent, TabletToolHandle, + TabletToolTarget, UpEvent as ToolUpEvent, }, }, touch::{ @@ -306,6 +306,16 @@ impl PointerFocusTarget { session.set_cursor_hotspot(cursor_hotspot); } } + + pub fn supports_tool(&self, tool_handle: &TabletToolHandle) -> bool { + match self { + Self::WlSurface { surface, .. } if surface.client().is_some() => tool_handle + .client_tools(&surface.client().unwrap()) + .next() + .is_some(), + _ => true, + } + } } impl KeyboardFocusTarget { From 67aa6a0c49ae818d10a3662362e9892c1fe9ac89 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 18 Aug 2026 19:14:28 +0200 Subject: [PATCH 49/80] input: Don't send out pointer events for tool events --- src/input/mod.rs | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 28877356..8730e66b 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1598,15 +1598,7 @@ impl State { std::mem::drop(shell); let pointer = seat.get_pointer().unwrap(); - pointer.motion( - self, - under.clone(), - &PointerMotionEvent { - location: position.as_logical(), - serial: SERIAL_COUNTER.next_serial(), - time: InputTime::now(), - }, - ); + pointer.set_location(position.as_logical()); let tablet_seat = seat.tablet_seat(); @@ -1686,15 +1678,7 @@ impl State { std::mem::drop(shell); let pointer = seat.get_pointer().unwrap(); - pointer.motion( - self, - under.clone(), - &PointerMotionEvent { - location: position.as_logical(), - serial: SERIAL_COUNTER.next_serial(), - time: InputTime::now(), - }, - ); + pointer.set_location(position.as_logical()); let tablet_seat = seat.tablet_seat(); From a66f629a4e59c8b2c2a17163bdca126a8dd12666 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 18 Aug 2026 19:15:03 +0200 Subject: [PATCH 50/80] input: Make sure to update internal pointer position state on tool input --- src/input/mod.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/input/mod.rs b/src/input/mod.rs index 8730e66b..fe210612 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1590,6 +1590,7 @@ impl State { return; }; + let current_output = seat.active_output(); let position = transform_output_mapped_position(&output, &event, shell.zoom_state()); let under = State::surface_under(position, &output, &shell) @@ -1652,6 +1653,29 @@ impl State { tool.frame(self, event.time()); } + + let mut shell = self.common.shell.write(); + shell.update_pointer_position(position.to_local(&output), &output); + shell.update_focal_point( + &seat, + position, + self.common.config.cosmic_conf.accessibility_zoom.view_moves, + ); + + if output != current_output { + for session in cursor_sessions_for_output(&shell, ¤t_output) { + session.set_cursor_pos(None); + } + seat.set_active_output(&output); + } + + update_output_image_copy_cursor_position( + &shell, + &self.common.clock, + &output, + &seat, + position, + ); } } InputEvent::TabletToolProximity { event, .. } => { @@ -1670,6 +1694,7 @@ impl State { return; }; + let current_output = seat.active_output(); let position = transform_output_mapped_position(&output, &event, shell.zoom_state()); let under = State::surface_under(position, &output, &shell) @@ -1761,6 +1786,29 @@ impl State { tool.frame(self, event.time()); } + + let mut shell = self.common.shell.write(); + shell.update_pointer_position(position.to_local(&output), &output); + shell.update_focal_point( + &seat, + position, + self.common.config.cosmic_conf.accessibility_zoom.view_moves, + ); + + if output != current_output { + for session in cursor_sessions_for_output(&shell, ¤t_output) { + session.set_cursor_pos(None); + } + seat.set_active_output(&output); + } + + update_output_image_copy_cursor_position( + &shell, + &self.common.clock, + &output, + &seat, + position, + ); } } InputEvent::TabletToolTip { event, .. } => { From 85564f033699e6371600da552a7ee0e889d8114e Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 18 Aug 2026 19:15:27 +0200 Subject: [PATCH 51/80] input: Focus on tool tip down --- src/input/mod.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/input/mod.rs b/src/input/mod.rs index fe210612..11466266 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1834,6 +1834,21 @@ impl State { if let Some(seat) = maybe_seat { self.common.idle_notifier_state.notify_activity(&seat); notify_cursor_activity(self, &seat); + + let serial = SERIAL_COUNTER.next_serial(); + let output = seat.active_output(); + let shell = self.common.shell.write(); + let position = + transform_output_mapped_position(&output, &event, shell.zoom_state()); + let under = State::element_under(position, &output, &shell, &seat); + drop(shell); + + if event.tip_state() == TabletToolTipState::Down + && let Some(target) = under.as_ref() + { + Shell::set_focus(self, Some(target), &seat, Some(serial), false); + } + if let Some(tool) = seat.tablet_seat().get_tool(&event.tool()) { let serial = SERIAL_COUNTER.next_serial(); match event.tip_state() { From e65f6608a46f5c8b1250623a9542c2a5214ed285 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 18 Aug 2026 19:16:23 +0200 Subject: [PATCH 52/80] element: Remove unnecessary grab variants --- src/shell/element/stack.rs | 30 ++++++------------------------ src/shell/element/window.rs | 15 +++------------ 2 files changed, 9 insertions(+), 36 deletions(-) diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 34e6f876..17397153 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -1723,19 +1723,10 @@ impl PointerTarget for CosmicStack { false, ); if let Some((grab, focus)) = res { - match grab.grab_type() { - GrabType::Touch => { - seat.get_touch().unwrap().set_grab(state, grab, serial) - } - GrabType::Pointer => seat - .get_pointer() + if let GrabType::Pointer = grab.grab_type() { + seat.get_pointer() .unwrap() - .set_grab(state, grab, serial, focus), - GrabType::TabletTool => seat - .tablet_seat() - .get_tool(grab.tool().unwrap()) - .unwrap() - .set_grab(state, grab, InputTime::now(), serial, focus), + .set_grab(state, grab, serial, focus); } } }); @@ -1793,19 +1784,10 @@ impl PointerTarget for CosmicStack { false, ); if let Some((grab, focus)) = res { - match grab.grab_type() { - GrabType::Touch => { - seat.get_touch().unwrap().set_grab(state, grab, serial) - } - GrabType::Pointer => seat - .get_pointer() + if let GrabType::Pointer = grab.grab_type() { + seat.get_pointer() .unwrap() - .set_grab(state, grab, serial, focus), - GrabType::TabletTool => seat - .tablet_seat() - .get_tool(grab.tool().unwrap()) - .unwrap() - .set_grab(state, grab, InputTime::now(), serial, focus), + .set_grab(state, grab, serial, focus); } } }); diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 846d9da4..903a2c05 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -1114,19 +1114,10 @@ impl PointerTarget for CosmicWindow { ); if let Some((grab, focus)) = res { - match grab.grab_type() { - GrabType::Touch => { - seat.get_touch().unwrap().set_grab(state, grab, serial) - } - GrabType::Pointer => seat - .get_pointer() + if let GrabType::Pointer = grab.grab_type() { + seat.get_pointer() .unwrap() - .set_grab(state, grab, serial, focus), - GrabType::TabletTool => seat - .tablet_seat() - .get_tool(grab.tool().unwrap()) - .unwrap() - .set_grab(state, grab, InputTime::now(), serial, focus), + .set_grab(state, grab, serial, focus) } } }); From b8824128a041ebbf7ceaf660f9a86756cfb4052f Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 18 Aug 2026 19:17:27 +0200 Subject: [PATCH 53/80] element: Add cursor shape and resize handling on tool input --- src/shell/element/stack.rs | 77 +++++++++++++++++++++++++++++++++++-- src/shell/element/window.rs | 74 +++++++++++++++++++++++++++++++++-- 2 files changed, 144 insertions(+), 7 deletions(-) diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 17397153..5ab022d9 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -1938,7 +1938,17 @@ impl TabletToolTarget for CosmicStack { data: &mut State, tool_descriptor: &TabletToolDescriptor, ) { - TabletToolTarget::proximity_out(&self.0, seat, data, tool_descriptor) + self.0.with_program(|p| { + let mut cursor_state = seat + .user_data() + .get::() + .unwrap() + .lock() + .unwrap(); + cursor_state.unset_shape(); + let _previous = p.swap_focus(None); + }); + TabletToolTarget::proximity_out(&self.0, seat, data, tool_descriptor); } fn down( @@ -1948,7 +1958,50 @@ impl TabletToolTarget for CosmicStack { tool_descriptor: &TabletToolDescriptor, event: &ToolDownEvent, ) { - TabletToolTarget::down(&self.0, seat, data, tool_descriptor, event) + match self.0.with_program(|p| p.current_focus()) { + Some(Focus::Header) => { + TabletToolTarget::down(&self.0, seat, data, tool_descriptor, event) + } + Some(x) => { + let serial = event.serial; + let seat = seat.clone(); + let Some(surface) = self.0.with_program(|p| { + let window = &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]; + window.wl_surface().map(Cow::into_owned) + }) else { + return; + }; + self.0.loop_handle().insert_idle(move |state| { + let res = state.common.shell.write().resize_request( + &surface, + &seat, + serial, + match x { + Focus::ResizeTop => ResizeEdge::TOP, + Focus::ResizeTopLeft => ResizeEdge::TOP_LEFT, + Focus::ResizeTopRight => ResizeEdge::TOP_RIGHT, + Focus::ResizeBottom => ResizeEdge::BOTTOM, + Focus::ResizeBottomLeft => ResizeEdge::BOTTOM_LEFT, + Focus::ResizeBottomRight => ResizeEdge::BOTTOM_RIGHT, + Focus::ResizeLeft => ResizeEdge::LEFT, + Focus::ResizeRight => ResizeEdge::RIGHT, + Focus::Header => unreachable!(), + }, + state.common.config.cosmic_conf.edge_snap_threshold, + false, + ); + if let Some((grab, focus)) = res { + if let GrabType::TabletTool = grab.grab_type() { + seat.tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(state, grab, InputTime::now(), serial, focus); + } + } + }); + } + None => {} + } } fn up( @@ -1969,12 +2022,30 @@ impl TabletToolTarget for CosmicStack { event: &ToolMotionEvent, ) { let mut event = event.clone(); + self.0.with_program(|p| { + let active = p.active.load(Ordering::SeqCst); + let active_window = &p.windows.lock().unwrap()[active]; + let Some(next) = Focus::under(active_window, TAB_HEIGHT, event.location) else { + return; + }; + let _previous = p.swap_focus(Some(next)); + + let mut cursor_state = seat + .user_data() + .get::() + .unwrap() + .lock() + .unwrap(); + cursor_state.set_shape(next.cursor_shape()); + seat.set_cursor_image_status(CursorImageStatus::default_named()); + }); + 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(); - TabletToolTarget::motion(&self.0, seat, data, tool_descriptor, &event); + TabletToolTarget::motion(&self.0, seat, data, tool_descriptor, &event); if event.location.y < 0.0 || event.location.y > TAB_HEIGHT as f64 || event.location.x < 64.0 diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 903a2c05..6488ffa3 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -1032,8 +1032,7 @@ impl PointerTarget for CosmicWindow { return; }; - let old_focus = p.swap_focus(Some(next)); - assert_eq!(old_focus, None); + let _ = p.swap_focus(Some(next)); let cursor_state = seat.user_data().get::().unwrap(); cursor_state.lock().unwrap().set_shape(next.cursor_shape()); @@ -1270,6 +1269,11 @@ impl TabletToolTarget for CosmicWindow { data: &mut State, tool_descriptor: &TabletToolDescriptor, ) { + self.0.with_program(|p| { + let cursor_state = seat.user_data().get::().unwrap(); + cursor_state.lock().unwrap().unset_shape(); + let _previous = p.swap_focus(None); + }); TabletToolTarget::proximity_out(&self.0, seat, data, tool_descriptor) } @@ -1280,7 +1284,49 @@ impl TabletToolTarget for CosmicWindow { tool_descriptor: &TabletToolDescriptor, event: &ToolDownEvent, ) { - TabletToolTarget::down(&self.0, seat, data, tool_descriptor, event) + match self.0.with_program(|p| p.current_focus()) { + Some(Focus::Header) => { + TabletToolTarget::down(&self.0, seat, data, tool_descriptor, event) + } + Some(x) => { + let serial = event.serial; + let seat = seat.clone(); + let Some(surface) = self.wl_surface().map(Cow::into_owned) else { + return; + }; + + self.0.loop_handle().insert_idle(move |state| { + let res = state.common.shell.write().resize_request( + &surface, + &seat, + serial, + match x { + Focus::ResizeTop => ResizeEdge::TOP, + Focus::ResizeTopLeft => ResizeEdge::TOP_LEFT, + Focus::ResizeTopRight => ResizeEdge::TOP_RIGHT, + Focus::ResizeBottom => ResizeEdge::BOTTOM, + Focus::ResizeBottomLeft => ResizeEdge::BOTTOM_LEFT, + Focus::ResizeBottomRight => ResizeEdge::BOTTOM_RIGHT, + Focus::ResizeLeft => ResizeEdge::LEFT, + Focus::ResizeRight => ResizeEdge::RIGHT, + Focus::Header => unreachable!(), + }, + state.common.config.cosmic_conf.edge_snap_threshold, + false, + ); + + if let Some((grab, focus)) = res { + if let GrabType::TabletTool = grab.grab_type() { + seat.tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(state, grab, InputTime::now(), serial, focus) + } + } + }); + } + None => {} + } } fn up( @@ -1300,7 +1346,27 @@ impl TabletToolTarget for CosmicWindow { tool_descriptor: &TabletToolDescriptor, event: &ToolMotionEvent, ) { - TabletToolTarget::motion(&self.0, seat, data, tool_descriptor, event) + let mut event = event.clone(); + self.0.with_program(|p| { + let has_ssd = p.has_ssd(false); + if has_ssd || p.has_tiled_state() { + let Some(next) = Focus::under( + &p.window, + if has_ssd { SSD_HEIGHT } else { 0 }, + event.location, + ) else { + return; + }; + let _previous = p.swap_focus(Some(next)); + + let cursor_state = seat.user_data().get::().unwrap(); + cursor_state.lock().unwrap().set_shape(next.cursor_shape()); + seat.set_cursor_image_status(CursorImageStatus::default_named()); + } + }); + + event.location -= self.0.with_program(|p| p.window.geometry().loc.to_f64()); + TabletToolTarget::motion(&self.0, seat, data, tool_descriptor, &event) } fn axis( From f00ff834c96f8154dbe11f983afc93b8b7d71c8d Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 18 Aug 2026 19:18:16 +0200 Subject: [PATCH 54/80] resize/tiling: Fix location for tablet tools --- src/shell/layout/tiling/grabs/resize.rs | 17 +++++------------ src/shell/layout/tiling/mod.rs | 2 -- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/shell/layout/tiling/grabs/resize.rs b/src/shell/layout/tiling/grabs/resize.rs index b6452168..6bb24929 100644 --- a/src/shell/layout/tiling/grabs/resize.rs +++ b/src/shell/layout/tiling/grabs/resize.rs @@ -1,7 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-only -use std::cell::Cell; - use crate::{ backend::render::cursor::CursorState, shell::{ @@ -52,7 +50,6 @@ pub struct ResizeForkTarget { pub output: WeakOutput, pub left_up_idx: usize, pub orientation: Orientation, - pub last_tablet_location: Cell>>, } impl IsAlive for ResizeForkTarget { @@ -193,10 +190,9 @@ impl TabletToolTarget for ResizeForkTarget { let serial = event.serial; let time = event.time; let tool = tool_descriptor.clone(); - let Some(location) = self.last_tablet_location.take() else { - return; - }; data.common.event_loop_handle.insert_idle(move |state| { + let pointer = seat.get_pointer().unwrap(); + let location = pointer.current_location(); let tablet = seat.tablet_seat().get_tool(&tool).unwrap(); tablet.set_grab( state, @@ -205,11 +201,11 @@ impl TabletToolTarget for ResizeForkTarget { tool, data: TabletToolGrabStartData { focus: None, - location: location.as_logical(), + location: location, trigger: TabletToolGrabTrigger::Tip, }, }, - location, + location.as_global(), node, left_up_idx, orientation, @@ -227,10 +223,8 @@ impl TabletToolTarget for ResizeForkTarget { _seat: &Seat, _data: &mut State, _tool_descriptor: &TabletToolDescriptor, - event: &TabletMotionEvent, + _event: &TabletMotionEvent, ) { - self.last_tablet_location - .set(Some(event.location.as_global())) } fn proximity_out( &self, @@ -238,7 +232,6 @@ impl TabletToolTarget for ResizeForkTarget { _data: &mut State, _tool_descriptor: &TabletToolDescriptor, ) { - self.last_tablet_location.set(None); } fn proximity_in( diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 2f694acd..8706dc59 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -67,7 +67,6 @@ use smithay::{ wayland::{compositor::add_blocker, seat::WaylandFocus}, }; use std::{ - cell::Cell, collections::{HashMap, VecDeque}, sync::{Arc, Weak}, time::{Duration, Instant}, @@ -3349,7 +3348,6 @@ impl TilingLayout { output: self.output.downgrade(), left_up_idx: idx, orientation, - last_tablet_location: Cell::new(None), } .into(), (last_geometry.loc From 87d73dbce32f9571b458f0934f40bff12eb665f5 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 18 Aug 2026 19:23:22 +0200 Subject: [PATCH 55/80] resize/tiling: Set cursor icons on tool input --- src/shell/layout/tiling/grabs/resize.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/shell/layout/tiling/grabs/resize.rs b/src/shell/layout/tiling/grabs/resize.rs index 6bb24929..850545be 100644 --- a/src/shell/layout/tiling/grabs/resize.rs +++ b/src/shell/layout/tiling/grabs/resize.rs @@ -228,20 +228,32 @@ impl TabletToolTarget for ResizeForkTarget { } fn proximity_out( &self, - _seat: &Seat, + seat: &Seat, _data: &mut State, _tool_descriptor: &TabletToolDescriptor, ) { + let user_data = seat.user_data(); + let cursor_state = user_data.get::().unwrap(); + cursor_state.lock().unwrap().unset_shape(); } fn proximity_in( &self, - _seat: &Seat, + seat: &Seat, _data: &mut State, _tool_descriptor: &TabletToolDescriptor, _tablet: &Tablet, _serial: Serial, ) { + let user_data = seat.user_data(); + let cursor_state = user_data.get::().unwrap(); + cursor_state + .lock() + .unwrap() + .set_shape(match self.orientation { + Orientation::Horizontal => CursorIcon::RowResize, + Orientation::Vertical => CursorIcon::ColResize, + }); } fn up( &self, From bb95a57f5a59f594603cd62ba2df43f3ce713a95 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 18 Aug 2026 19:24:02 +0200 Subject: [PATCH 56/80] move: Make sure to always unset tool grab --- src/shell/grabs/moving.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index 15225212..50fc9740 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -745,6 +745,7 @@ impl TabletToolGrab for MoveGrab { event: &ProximityOutEvent, ) { handle.proximity_out(data, event); + handle.unset_grab(self, data, event.serial, event.time, false); } fn motion( @@ -777,11 +778,8 @@ impl TabletToolGrab for MoveGrab { handle: &mut TabletToolInnerHandle<'_, State>, event: &TabletUpEvent, ) { - if self.tool().is_some_and(|tool| tool == handle.descriptor()) { - handle.unset_grab(self, data, event.serial, event.time, false); - } - handle.up(data, event); + handle.unset_grab(self, data, event.serial, event.time, false); } fn button( From 6bf75e1c1006b4be39e473883385e5a9273862dc Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 18 Aug 2026 19:24:27 +0200 Subject: [PATCH 57/80] shell: Handle Tablet grabs --- src/shell/mod.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index b10d9d63..5bccf797 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -49,7 +49,7 @@ use smithay::{ pointer::{ CursorImageStatus, CursorImageSurfaceData, Focus, GrabStartData as PointerGrabStartData, }, - tablet::tool::GrabTrigger as TabletGrabTrigger, + tablet::{TabletSeatTrait, tool::GrabTrigger as TabletGrabTrigger}, }, output::{Output, WeakOutput}, reexports::{ @@ -5164,10 +5164,19 @@ pub fn check_grab_preconditions( let pointer = seat.get_pointer().unwrap(); let touch = seat.get_touch().unwrap(); + let tablet = seat.tablet_seat(); + let tools = tablet.get_tools(); let start_data = if serial.is_some_and(|serial| touch.has_grab(serial)) { GrabStartData::Touch(touch.grab_start_data().unwrap()) + } else if let Some((desc, tool)) = + serial.and_then(|serial| tools.iter().find(|(_, tool)| tool.has_grab(serial))) + { + GrabStartData::TabletTool { + tool: desc.clone(), + data: tool.grab_start_data().unwrap(), + } } else { GrabStartData::Pointer(pointer.grab_start_data().unwrap_or_else(|| { PointerGrabStartData { From 0e15b14ab29f75da8023efe85a0e5b1e4a293af7 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 18 Aug 2026 19:24:54 +0200 Subject: [PATCH 58/80] utils/iced: Fix tablet serials and buttons --- src/utils/iced/mod.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/utils/iced/mod.rs b/src/utils/iced/mod.rs index 8680bed3..4522644b 100644 --- a/src/utils/iced/mod.rs +++ b/src/utils/iced/mod.rs @@ -497,16 +497,18 @@ impl IcedElementInternal

{ impl TabletToolTarget for IcedElement

{ fn proximity_in( &self, - _seat: &Seat, + seat: &Seat, _data: &mut crate::state::State, _tool_descriptor: &TabletToolDescriptor, _tablet: &Tablet, - _serial: Serial, + serial: Serial, ) { let mut internal = self.0.lock().unwrap(); internal .state .queue_event(Event::Mouse(MouseEvent::CursorEntered)); + internal.last_tablet_serial = Some(serial); + *internal.last_seat.lock().unwrap() = Some((seat.clone(), serial)); internal.update(false); } @@ -578,7 +580,8 @@ impl TabletToolTarget for Iced .state .queue_event(Event::Mouse(MouseEvent::CursorMoved { position })); internal.cursor_pos = Some(event_location); - *internal.last_seat.lock().unwrap() = Some((seat.clone(), event.serial)); + *internal.last_seat.lock().unwrap() = + Some((seat.clone(), internal.last_tablet_serial.unwrap())); internal.update(false); } @@ -600,10 +603,9 @@ impl TabletToolTarget for Iced ) { let mut internal = self.0.lock().unwrap(); let button = match event.button { - 0x110 => MouseButton::Left, - 0x111 => MouseButton::Right, - 0x112 => MouseButton::Middle, - x => MouseButton::Other(x as u16), + 0x14b => MouseButton::Right, + 0x14c => MouseButton::Middle, + _ => return, }; internal.state.queue_event(Event::Mouse(match event.state { ButtonState::Pressed => MouseEvent::ButtonPressed(button), From 2a3b4dca5f0569b01e2daa52e928def4949eeb25 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 19 Aug 2026 17:24:04 +0200 Subject: [PATCH 59/80] shell: fix client initiated tablet grabs --- src/shell/mod.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 5bccf797..47d78851 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -5190,8 +5190,16 @@ pub fn check_grab_preconditions( if let Some(surface) = client_initiated { // Check that this surface has a click or touch down grab. if !match serial { - Some(serial) => pointer.has_grab(serial) || touch.has_grab(serial), - None => pointer.is_grabbed() | touch.is_grabbed(), + Some(serial) => { + pointer.has_grab(serial) + || touch.has_grab(serial) + || tools.values().any(|tool| tool.has_grab(serial)) + } + None => { + pointer.is_grabbed() + || touch.is_grabbed() + || tools.values().any(|tool| tool.is_grabbed()) + } } { return None; } From e563dc85bf28f927db0241c1dc83fd4afce233b2 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 19 Aug 2026 18:07:37 +0200 Subject: [PATCH 60/80] input: Don't update pointer position on proximity_out --- src/input/mod.rs | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 11466266..76d2c355 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1787,28 +1787,30 @@ impl State { tool.frame(self, event.time()); } - let mut shell = self.common.shell.write(); - shell.update_pointer_position(position.to_local(&output), &output); - shell.update_focal_point( - &seat, - position, - self.common.config.cosmic_conf.accessibility_zoom.view_moves, - ); + if event.state() == ProximityState::In { + let mut shell = self.common.shell.write(); + shell.update_pointer_position(position.to_local(&output), &output); + shell.update_focal_point( + &seat, + position, + self.common.config.cosmic_conf.accessibility_zoom.view_moves, + ); - if output != current_output { - for session in cursor_sessions_for_output(&shell, ¤t_output) { - session.set_cursor_pos(None); + if output != current_output { + for session in cursor_sessions_for_output(&shell, ¤t_output) { + session.set_cursor_pos(None); + } + seat.set_active_output(&output); } - seat.set_active_output(&output); - } - update_output_image_copy_cursor_position( - &shell, - &self.common.clock, - &output, - &seat, - position, - ); + update_output_image_copy_cursor_position( + &shell, + &self.common.clock, + &output, + &seat, + position, + ); + } } } InputEvent::TabletToolTip { event, .. } => { From 1c8a0070c023d41326fe8cc8b9367231446492bc Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 19 Aug 2026 18:07:58 +0200 Subject: [PATCH 61/80] element/stack: Handle touch (and tablet) inputs on tabs --- src/shell/element/stack/tab.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/shell/element/stack/tab.rs b/src/shell/element/stack/tab.rs index f8fda525..4b2a3292 100644 --- a/src/shell/element/stack/tab.rs +++ b/src/shell/element/stack/tab.rs @@ -8,6 +8,7 @@ use cosmic::{ layout::{Layout, Limits, Node}, mouse, overlay, renderer, text::{Ellipsize, EllipsizeHeightLimit, Shaping, Wrapping}, + touch, widget::{Id, Widget, operation::Operation, tree::Tree}, }, widget::{self, container::draw_background, rule::FillMode, scrollable::AbsoluteOffset}, @@ -377,6 +378,7 @@ where if matches!( event, event::Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + | event::Event::Touch(touch::Event::FingerPressed { .. }) ) && let Some(message) = self.press_message.clone() { shell.publish(message); @@ -395,6 +397,7 @@ where if matches!( event, event::Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) + | event::Event::Touch(touch::Event::FingerLifted { .. }) ) { shell.publish(Message::activate(self.idx)); shell.capture_event(); From bd0b83412890ec4e126e01c92cfbc87e6e2e78cd Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 19 Aug 2026 18:08:21 +0200 Subject: [PATCH 62/80] element/stack: Use correct serial for tablet grabs --- src/shell/element/stack.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 5ab022d9..a97f5778 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -121,6 +121,7 @@ pub struct CosmicStackInternal { previous_keyboard: AtomicUsize, pointer_entered: AtomicU8, touch_serial: AtomicU32, + tablet_serial: AtomicU32, reenter: AtomicBool, potential_drag: Mutex>, override_alive: AtomicBool, @@ -180,6 +181,7 @@ impl CosmicStack { previous_keyboard: AtomicUsize::new(0), pointer_entered: AtomicU8::new(0), touch_serial: AtomicU32::new(0), + tablet_serial: AtomicU32::new(0), reenter: AtomicBool::new(false), potential_drag: Mutex::new(None), override_alive: AtomicBool::new(true), @@ -1958,12 +1960,19 @@ impl TabletToolTarget for CosmicStack { tool_descriptor: &TabletToolDescriptor, event: &ToolDownEvent, ) { + self.0.with_program(|p| { + p.tablet_serial + .store(event.serial.into(), Ordering::Release) + }); match self.0.with_program(|p| p.current_focus()) { Some(Focus::Header) => { TabletToolTarget::down(&self.0, seat, data, tool_descriptor, event) } Some(x) => { - let serial = event.serial; + let serial = self + .0 + .with_program(|p| p.tablet_serial.load(Ordering::Acquire)) + .into(); let seat = seat.clone(); let Some(surface) = self.0.with_program(|p| { let window = &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]; @@ -2051,7 +2060,13 @@ impl TabletToolTarget for CosmicStack { || event.location.x < 64.0 || event.location.x > (active_window_geo.size.w as f64 - 64.0) { - self.start_drag(data, seat, event.serial); + self.start_drag( + data, + seat, + self.0 + .with_program(|p| p.tablet_serial.load(Ordering::Acquire)) + .into(), + ); } } From f8344126a16e7f8712fa123076fc34c9238493a7 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 19 Aug 2026 18:12:11 +0200 Subject: [PATCH 63/80] input: Update the pointer state on absolute pointer events as well --- src/input/mod.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 76d2c355..45586b03 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -767,15 +767,25 @@ impl State { ); ptr.frame(self); + let mut shell = self.common.shell.write(); // Keep the seat's active output following the pointer. Click-to- // focus (PointerButton) resolves its target via // `seat.active_output()` let previous_output = seat.active_output(); if previous_output != output { + for session in cursor_sessions_for_output(&shell, &previous_output) { + session.set_cursor_pos(None); + } seat.set_active_output(&output); } - let shell = self.common.shell.read(); + shell.update_pointer_position(position.to_local(&output), &output); + shell.update_focal_point( + &seat, + position, + self.common.config.cosmic_conf.accessibility_zoom.view_moves, + ); + update_output_image_copy_cursor_position( &shell, &self.common.clock, From e8caf335b39a53c0193de005ef913258bcaaac03 Mon Sep 17 00:00:00 2001 From: Hojjat Date: Thu, 3 Sep 2026 11:50:50 -0600 Subject: [PATCH 64/80] refactor: align the corner radii order with the theme It's [TL, TR, BR, BL] everywhere now --- src/backend/render/mod.rs | 4 +-- src/backend/render/shadow.rs | 6 ---- src/backend/render/wayland/blur_effect.rs | 4 +-- src/backend/render/wayland/clipped_surface.rs | 10 +++---- src/shell/element/stack.rs | 8 +++--- src/shell/element/window.rs | 28 +++++++++---------- 6 files changed, 27 insertions(+), 33 deletions(-) diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index b348640f..1be5b9d4 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -286,10 +286,10 @@ impl IndicatorShader { Uniform::new( "radius", [ - outer_radius[3] as f32, - outer_radius[1] as f32, outer_radius[0] as f32, + outer_radius[1] as f32, outer_radius[2] as f32, + outer_radius[3] as f32, ], ), Uniform::new("scale", scale as f32), diff --git a/src/backend/render/shadow.rs b/src/backend/render/shadow.rs index 37b1fdec..87fdd2e3 100644 --- a/src/backend/render/shadow.rs +++ b/src/backend/render/shadow.rs @@ -86,12 +86,6 @@ impl ShadowShader { let offset = [0., 5.]; let color = [0., 0., 0., if dark_mode { 0.45 } else { 0.35 }]; let radius = radius.map(|r| ceil(r as f64)); - let radius = [ - radius[3], // top_left - radius[1], // top_right - radius[0], // bottom_right - radius[2], // bottom_left - ]; let width = softness; let sigma = width / 2.; diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index c4d27fac..b8a7935a 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -247,10 +247,10 @@ impl BlurElement { Uniform::new( "corner_radius", [ - radii[3] as f32, - radii[1] as f32, radii[0] as f32, + radii[1] as f32, radii[2] as f32, + radii[3] as f32, ], ), Uniform::new( diff --git a/src/backend/render/wayland/clipped_surface.rs b/src/backend/render/wayland/clipped_surface.rs index 8322079e..52535269 100644 --- a/src/backend/render/wayland/clipped_surface.rs +++ b/src/backend/render/wayland/clipped_surface.rs @@ -99,10 +99,10 @@ where Uniform::new( "corner_radius", [ - radius[3] as f32, - radius[1] as f32, radius[0] as f32, + radius[1] as f32, radius[2] as f32, + radius[3] as f32, ], ), Uniform::new( @@ -145,10 +145,10 @@ where geo: Rectangle, radius: [u8; 4], ) -> [Rectangle; 4] { - let top_left = radius[3] as f64; + let top_left = radius[0] as f64; let top_right = radius[1] as f64; - let bottom_right = radius[0] as f64; - let bottom_left = radius[2] as f64; + let bottom_right = radius[2] as f64; + let bottom_left = radius[3] as f64; [ Rectangle::new(geo.loc, Size::from((top_left, top_left))), diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index a97f5778..c973b189 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -842,7 +842,7 @@ impl CosmicStack { ))); }; - let radii = radii.map(|[a, _, c, _]| [a, 0, c, 0]); + let radii = radii.map(|[_, _, c, d]| [0, 0, c, d]); windows[active].push_render_elements( renderer, window_loc, @@ -1015,17 +1015,17 @@ impl CosmicStack { .corner_radius(geometry_size) .unwrap_or([default_radius; 4]); + corners[0] = 0; corners[1] = 0; - corners[3] = 0; corners } else { let mut corners = active_window.corner_radius(geometry_size).unwrap_or(radii); - corners[0] = radii[0].max(corners[0]); + corners[0] = radii[0]; corners[1] = radii[1]; corners[2] = radii[2].max(corners[2]); - corners[3] = radii[3]; + corners[3] = radii[3].max(corners[3]); corners } diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 6488ffa3..91423ca5 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -438,12 +438,12 @@ impl CosmicWindow { .map(|x| (x * scale as f32).round() as u8); if has_ssd && !clip { // bottom corners - radii[0] = 0; radii[2] = 0; + radii[3] = 0; if is_tiled { // top corners + radii[0] = 0; radii[1] = 0; - radii[3] = 0; } } @@ -510,12 +510,12 @@ impl CosmicWindow { && !is_maximized; if has_ssd && !clip { // bottom corners - radii[0] = 0; radii[2] = 0; + radii[3] = 0; if is_tiled { // top corners + radii[0] = 0; radii[1] = 0; - radii[3] = 0; } } @@ -560,8 +560,8 @@ impl CosmicWindow { self.0.with_program(|p| { let mut radii = radii; if has_ssd { + radii[0] = 0; radii[1] = 0; - radii[3] = 0; } let theme = p.theme.lock().unwrap(); let frosted = if theme.cosmic().frosted_windows { @@ -585,8 +585,8 @@ impl CosmicWindow { }); if has_ssd { - radii[0] = 0; radii[2] = 0; + radii[3] = 0; let ssd_loc = location + self .0 @@ -691,26 +691,26 @@ impl CosmicWindow { (has_ssd, true) => { let mut corners = p.window.corner_radius(geometry_size).unwrap_or(radii); - corners[0] = radii[0].max(corners[0]); + corners[0] = if has_ssd { + radii[0] + } else { + radii[0].max(corners[0]) + }; corners[1] = if has_ssd { radii[1] } else { radii[1].max(corners[1]) }; corners[2] = radii[2].max(corners[2]); - corners[3] = if has_ssd { - radii[3] - } else { - radii[3].max(corners[3]) - }; + corners[3] = radii[3].max(corners[3]); corners } (true, false) => p .window .corner_radius(geometry_size) - .map(|[a, _, c, _]| [a, radii[1], c, radii[3]]) - .unwrap_or([default_radius, radii[1], default_radius, radii[3]]), + .map(|[_, _, c, d]| [radii[0], radii[1], c, d]) + .unwrap_or([radii[0], radii[1], default_radius, default_radius]), (false, false) => p .window .corner_radius(geometry_size) From 057e7b72cc0a1da4cbed73c51f30660255ba2ea9 Mon Sep 17 00:00:00 2001 From: Hojjat Date: Thu, 3 Sep 2026 11:51:49 -0600 Subject: [PATCH 65/80] chore: remove the gaurd against large paddings --- src/backend/render/mod.rs | 3 +-- src/wayland/handlers/corner_radius.rs | 11 ++--------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 1be5b9d4..9ad61588 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -899,8 +899,7 @@ where let geometry = geometry.to_local(output).as_logical(); let padded = with_states(layer.wl_surface(), |states| { - surface_padding(states, geometry.size) - .and_then(|padding| pad_rect(geometry, &padding)) + surface_padding(states).and_then(|padding| pad_rect(geometry, &padding)) }) .unwrap_or(geometry); let radii = with_states(layer.wl_surface(), |states| { diff --git a/src/wayland/handlers/corner_radius.rs b/src/wayland/handlers/corner_radius.rs index ded8d923..1a541938 100644 --- a/src/wayland/handlers/corner_radius.rs +++ b/src/wayland/handlers/corner_radius.rs @@ -45,18 +45,11 @@ pub fn surface_corners(states: &SurfaceData, size: Size) -> Option ]) } -pub fn surface_padding(states: &SurfaceData, size: Size) -> Option<[i32; 4]> { +pub fn surface_padding(states: &SurfaceData) -> Option<[i32; 4]> { let mut guard = states.cached_state.get::(); let padding = guard.current().0?; - - // guard against padding being too large - Some([ - padding.top.min(size.h / 2), - padding.right.min(size.w / 2), - padding.bottom.min(size.h / 2), - padding.left.min(size.w / 2), - ]) + Some([padding.top, padding.right, padding.bottom, padding.left]) } pub fn pad_rect( From 0f56d214b9ceaa53ca12ef6ce7043e07626bb42c Mon Sep 17 00:00:00 2001 From: Hojjat Date: Fri, 4 Sep 2026 07:46:03 -0600 Subject: [PATCH 66/80] chore: clippy fixes --- src/input/tablet_emu.rs | 48 ++++++++++----------- src/shell/element/stack.rs | 38 ++++++++-------- src/shell/element/window.rs | 26 +++++------ src/shell/grabs/moving.rs | 2 +- src/shell/layout/tiling/grabs/resize.rs | 2 +- src/wayland/handlers/decoration.rs | 21 +++++---- src/wayland/handlers/pointer_constraints.rs | 1 - 7 files changed, 66 insertions(+), 72 deletions(-) diff --git a/src/input/tablet_emu.rs b/src/input/tablet_emu.rs index 94d31d89..6ab17924 100644 --- a/src/input/tablet_emu.rs +++ b/src/input/tablet_emu.rs @@ -105,19 +105,17 @@ impl TabletToolGrab for PointerEmulationGrab { InputTime::now(), true, ); - } else { - if let Some(pointer) = self.seat.get_pointer() { - pointer.motion( - data, - focus, - &pointer::MotionEvent { - location: event.location, - serial: SERIAL_COUNTER.next_serial(), - time: InputTime::now(), - }, - ); - pointer.frame(data); - } + } else if let Some(pointer) = self.seat.get_pointer() { + pointer.motion( + data, + focus, + &pointer::MotionEvent { + location: event.location, + serial: SERIAL_COUNTER.next_serial(), + time: InputTime::now(), + }, + ); + pointer.frame(data); } } @@ -179,19 +177,17 @@ impl TabletToolGrab for PointerEmulationGrab { InputTime::now(), true, ); - } else { - if let Some(pointer) = self.seat.get_pointer() { - pointer.motion( - data, - focus, - &pointer::MotionEvent { - location: event.location, - serial: SERIAL_COUNTER.next_serial(), - time: InputTime::now(), - }, - ); - pointer.frame(data); - } + } else if let Some(pointer) = self.seat.get_pointer() { + pointer.motion( + data, + focus, + &pointer::MotionEvent { + location: event.location, + serial: SERIAL_COUNTER.next_serial(), + time: InputTime::now(), + }, + ); + pointer.frame(data); } } diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index c973b189..f4a37bfe 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -1724,12 +1724,12 @@ impl PointerTarget for CosmicStack { state.common.config.cosmic_conf.edge_snap_threshold, false, ); - if let Some((grab, focus)) = res { - if let GrabType::Pointer = grab.grab_type() { - seat.get_pointer() - .unwrap() - .set_grab(state, grab, serial, focus); - } + if let Some((grab, focus)) = res + && let GrabType::Pointer = grab.grab_type() + { + seat.get_pointer() + .unwrap() + .set_grab(state, grab, serial, focus); } }); } @@ -1785,12 +1785,12 @@ impl PointerTarget for CosmicStack { &state.common.event_loop_handle, false, ); - if let Some((grab, focus)) = res { - if let GrabType::Pointer = grab.grab_type() { - seat.get_pointer() - .unwrap() - .set_grab(state, grab, serial, focus); - } + if let Some((grab, focus)) = res + && let GrabType::Pointer = grab.grab_type() + { + seat.get_pointer() + .unwrap() + .set_grab(state, grab, serial, focus); } }); } @@ -1999,13 +1999,13 @@ impl TabletToolTarget for CosmicStack { state.common.config.cosmic_conf.edge_snap_threshold, false, ); - if let Some((grab, focus)) = res { - if let GrabType::TabletTool = grab.grab_type() { - seat.tablet_seat() - .get_tool(grab.tool().unwrap()) - .unwrap() - .set_grab(state, grab, InputTime::now(), serial, focus); - } + if let Some((grab, focus)) = res + && let GrabType::TabletTool = grab.grab_type() + { + seat.tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(state, grab, InputTime::now(), serial, focus); } }); } diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 91423ca5..fa4a1872 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -1112,12 +1112,12 @@ impl PointerTarget for CosmicWindow { false, ); - if let Some((grab, focus)) = res { - if let GrabType::Pointer = grab.grab_type() { - seat.get_pointer() - .unwrap() - .set_grab(state, grab, serial, focus) - } + if let Some((grab, focus)) = res + && let GrabType::Pointer = grab.grab_type() + { + seat.get_pointer() + .unwrap() + .set_grab(state, grab, serial, focus) } }); } @@ -1315,13 +1315,13 @@ impl TabletToolTarget for CosmicWindow { false, ); - if let Some((grab, focus)) = res { - if let GrabType::TabletTool = grab.grab_type() { - seat.tablet_seat() - .get_tool(grab.tool().unwrap()) - .unwrap() - .set_grab(state, grab, InputTime::now(), serial, focus) - } + if let Some((grab, focus)) = res + && let GrabType::TabletTool = grab.grab_type() + { + seat.tablet_seat() + .get_tool(grab.tool().unwrap()) + .unwrap() + .set_grab(state, grab, InputTime::now(), serial, focus) } }); } diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index 50fc9740..f1fbfa8a 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -905,7 +905,7 @@ impl Drop for MoveGrab { let seat = self.seat.clone(); // false-positive: `Output`s hash is based on it's inner ptr #[allow(clippy::mutable_key_type)] - let window_outputs = self.window_outputs.drain().collect::>(); + let window_outputs = std::mem::take(&mut self.window_outputs); let previous = self.previous; let window = self.window.clone(); let is_touch_grab = matches!(self.start_data, GrabStartData::Touch(_)); diff --git a/src/shell/layout/tiling/grabs/resize.rs b/src/shell/layout/tiling/grabs/resize.rs index 850545be..c6f202cf 100644 --- a/src/shell/layout/tiling/grabs/resize.rs +++ b/src/shell/layout/tiling/grabs/resize.rs @@ -201,7 +201,7 @@ impl TabletToolTarget for ResizeForkTarget { tool, data: TabletToolGrabStartData { focus: None, - location: location, + location, trigger: TabletToolGrabTrigger::Tip, }, }, diff --git a/src/wayland/handlers/decoration.rs b/src/wayland/handlers/decoration.rs index ae58d338..a1efbd0a 100644 --- a/src/wayland/handlers/decoration.rs +++ b/src/wayland/handlers/decoration.rs @@ -69,17 +69,16 @@ impl State { XdgMode::from_preference(self.default_decoration()) }; for (window, _) in mapped.windows() { - if PreferredDecorationMode::is_unset(&window.0) { - if let Some(toplevel) = window.0.toplevel() { - if toplevel.with_committed_state(|state| { - state.is_some_and(|state| state.decoration_mode.is_some()) - }) { - toplevel.with_pending_state(|state| { - state.decoration_mode = Some(mode); - }); - toplevel.send_configure(); - } - } + if PreferredDecorationMode::is_unset(&window.0) + && let Some(toplevel) = window.0.toplevel() + && toplevel.with_committed_state(|state| { + state.is_some_and(|state| state.decoration_mode.is_some()) + }) + { + toplevel.with_pending_state(|state| { + state.decoration_mode = Some(mode); + }); + toplevel.send_configure(); } } }; diff --git a/src/wayland/handlers/pointer_constraints.rs b/src/wayland/handlers/pointer_constraints.rs index e024bb40..743b3b0b 100644 --- a/src/wayland/handlers/pointer_constraints.rs +++ b/src/wayland/handlers/pointer_constraints.rs @@ -89,7 +89,6 @@ impl PointerConstraintsHandler for State { ConstraintRemove::PointerLeave(_) => { // If the constraint was broken by the pointer forcibly leaving the surface, then it doesn't // make much sense to warp it. - return; } ConstraintRemove::Destroyed(constraint) => { let Some(seat) = self From f61c0f9f76caa8640de4e2077ec31b6c37d12866 Mon Sep 17 00:00:00 2001 From: haydonryan Date: Tue, 1 Sep 2026 14:07:30 -0500 Subject: [PATCH 67/80] tabs: avoid two HashSet allocations in tab-change detection Replace the symmetric_difference over two freshly-materialized HashSets with direct contains_key lookups on the existing key sets. Semantically identical (set difference non-empty iff any key is in exactly one set) but removes two heap HashSet allocations plus hashing of every key on each widget update. Also drops the now-unused HashSet import. Implements compact-idiomatic-code-20 from the cosmic-comp optimization report. Created with the help of DeepSeek V4 Flash. --- src/shell/element/stack/tabs.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/shell/element/stack/tabs.rs b/src/shell/element/stack/tabs.rs index 86218db6..9dd5ef81 100644 --- a/src/shell/element/stack/tabs.rs +++ b/src/shell/element/stack/tabs.rs @@ -31,7 +31,7 @@ use keyframe::{ functions::{EaseInOutCubic, EaseOutCubic}, }; use std::{ - collections::{HashMap, HashSet, VecDeque}, + collections::{HashMap, VecDeque}, time::{Duration, Instant}, }; @@ -757,12 +757,10 @@ where state.last_state = Some(current_state.clone()); } let last_state = state.last_state.as_mut().unwrap(); - let unknown_keys = current_state - .keys() - .collect::>() - .symmetric_difference(&last_state.keys().collect::>()) - .next() - .is_some(); + let unknown_keys = current_state.len() != last_state.len() + || current_state + .keys() + .any(|key| !last_state.contains_key(key)); enum Difference { NewOrRemoved, From 8ad541dbe311ccef0e7be8b67df9f6d5f23039d0 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Tue, 8 Sep 2026 16:51:34 +0200 Subject: [PATCH 68/80] i18n: translation updates from weblate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Quentin PAGÈS Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-comp/oc/ Translation: Pop OS/COSMIC Comp --- resources/i18n/oc/cosmic_comp.ftl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/resources/i18n/oc/cosmic_comp.ftl b/resources/i18n/oc/cosmic_comp.ftl index e69de29b..a1238332 100644 --- a/resources/i18n/oc/cosmic_comp.ftl +++ b/resources/i18n/oc/cosmic_comp.ftl @@ -0,0 +1,20 @@ +a11y-zoom-move-continuously = La vista bolega de contunh amb lo ponchador +a11y-zoom-move-onedge = La lista bolega quand lo ponchador atenh un caire +a11y-zoom-move-centered = La vista bolega per gardar lo ponchador al centre +a11y-zoom-settings = Paramètres de la lópia... +unknown-keybinding = +window-menu-tiled = Fenèstras bandejanta +window-menu-screenshot = Prendre una captura d’ecran +window-menu-resize = Redimensionar +window-menu-move-prev-workspace = Desplaçar a l’espaci de trabalh precedent +window-menu-move-next-workspace = Desplaçar a l'espaci de trabalh seguent +window-menu-stack = Crear una pila de fenèstras +window-menu-unstack-all = Desapilar totas las fenèstras +window-menu-unstack = Desapilar la fenèstra +window-menu-sticky = Fenèstra adesiva +window-menu-close = Tampar +window-menu-close-all = Tampar totas las fenèstras +window-menu-resize-edge-top = Amont +window-menu-resize-edge-left = Esquèrra +window-menu-resize-edge-right = Drecha +window-menu-resize-edge-bottom = Aval From 7d1daab7bc5db7ef0c4be64af1e35c8a24acfa9b Mon Sep 17 00:00:00 2001 From: Aleksandr Kosachev Date: Sun, 6 Sep 2026 19:36:12 +0300 Subject: [PATCH 69/80] fix: remove foreign toplevel handle too --- src/wayland/protocols/toplevel_info.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/wayland/protocols/toplevel_info.rs b/src/wayland/protocols/toplevel_info.rs index 2dc76347..3146adc3 100644 --- a/src/wayland/protocols/toplevel_info.rs +++ b/src/wayland/protocols/toplevel_info.rs @@ -418,6 +418,12 @@ where handle.closed(); } } + // Remove the foreign handle too, so new subscribers cannot + // discover a toplevel whose window has already been destroyed. + if let Some(handle) = state.foreign_handle.take() { + self.foreign_toplevel_list.remove_toplevel(&handle); + } + *state = Default::default(); dirty = true; false } From a55785993e8ef6aad38862cb1a9e1ccaad3c340d Mon Sep 17 00:00:00 2001 From: apassert <249084898+apassert@users.noreply.github.com> Date: Wed, 2 Sep 2026 21:09:51 +0200 Subject: [PATCH 70/80] config: sort outputs by connector for deterministic fallback layout When no saved configuration matches the current output set, positions were assigned in hotplug/registration order. On hotplug races (e.g. reconnecting monitors through a KVM switch) outputs can appear in arbitrary order, producing a swapped left/right layout that then gets persisted to outputs.ron, poisoning all future reconnects. Sort outputs by connector name before generating fallback positions so the layout is deterministic regardless of arrival order. AI disclosure: implemented with the assistance of an LLM coding agent, directed, reviewed, and tested by the author. Signed-off-by: apassert <249084898+apassert@users.noreply.github.com> --- src/config/mod.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index d3d2919b..4d2fad9b 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -541,7 +541,13 @@ impl Config { primary.config_mut().xwayland_primary = true; } } - for output in outputs.iter().filter(|o| o.mirroring().is_none()) { + // sort by connector name for a deterministic layout independent of hotplug order + let mut sorted_outputs = outputs + .iter() + .filter(|o| o.mirroring().is_none()) + .collect::>(); + sorted_outputs.sort_by_key(|o| o.name()); + for output in sorted_outputs { { let mut config = output.config_mut(); config.position = (w, 0); From a103e7619b0a21b1c3c6b29debd6879d7d0bcc11 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Thu, 10 Sep 2026 18:12:37 +0200 Subject: [PATCH 71/80] chore: fige Cargo.lock --- Cargo.lock | 1322 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 1061 insertions(+), 261 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0a95294..a53353a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,61 @@ 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_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_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_windows", + "raw-window-handle", + "winit 0.31.0-beta.2", +] + [[package]] name = "addr2line" version = "0.25.1" @@ -61,6 +116,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" @@ -74,7 +135,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.11.0", + "bitflags 2.13.2", "cc", "cesu8", "jni", @@ -166,6 +227,25 @@ dependencies = [ "libloading", ] +[[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.5", + "serde", + "serde_repr", + "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus", +] + [[package]] name = "async-broadcast" version = "0.7.2" @@ -343,7 +423,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -390,9 +470,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" dependencies = [ "serde_core", ] @@ -412,13 +492,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]] @@ -463,6 +552,13 @@ dependencies = [ "num-traits", ] +[[package]] +name = "build_helpers" +version = "0.14.0" +dependencies = [ + "cfg_aliases", +] + [[package]] name = "bumpalo" version = "3.20.2" @@ -514,7 +610,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" dependencies = [ "async-task", - "bitflags 2.11.0", + "bitflags 2.13.2", "futures-core", "polling", "rustix 1.1.4", @@ -594,7 +690,7 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -615,7 +711,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", @@ -625,22 +720,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" @@ -650,7 +735,7 @@ dependencies = [ "bitflags 1.3.2", "block", "cocoa-foundation", - "core-foundation", + "core-foundation 0.9.4", "core-graphics", "foreign-types", "libc", @@ -665,8 +750,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", ] @@ -677,7 +762,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]] @@ -721,6 +808,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" @@ -734,8 +831,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", ] @@ -747,7 +844,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.13.2", + "core-foundation 0.10.1", "libc", ] @@ -760,12 +868,24 @@ dependencies = [ "libm", ] +[[package]] +name = "cosmic-client-toolkit" +version = "0.2.0" +dependencies = [ + "bitflags 2.13.2", + "cosmic-protocols", + "libc", + "smithay-client-toolkit", + "wayland-client", + "wayland-protocols", +] + [[package]] name = "cosmic-comp" version = "1.0.0" dependencies = [ "anyhow", - "bitflags 2.11.0", + "bitflags 2.13.2", "calloop", "clap_lex", "cosmic-comp-config", @@ -787,7 +907,7 @@ dependencies = [ "indexmap 2.13.0", "jiff", "keyframe", - "libcosmic", + "libcosmic-yoda", "libdisplay-info", "libsystemd", "log-panics", @@ -809,7 +929,7 @@ dependencies = [ "smithay", "smithay-egui", "thiserror 2.0.18", - "tiny-skia", + "tiny-skia 0.11.4", "tracing", "tracing-journald", "tracing-subscriber", @@ -839,7 +959,6 @@ dependencies = [ [[package]] name = "cosmic-config" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "atomicwrites", "calloop", @@ -860,7 +979,6 @@ dependencies = [ [[package]] name = "cosmic-config-derive" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "quote", "syn", @@ -869,12 +987,11 @@ dependencies = [ [[package]] name = "cosmic-freedesktop-icons" version = "0.4.0" -source = "git+https://github.com/pop-os/freedesktop-icons#ab4c57b8e416c6af9297cb04d101889896fd9a92" dependencies = [ "bstr", "btoi", "memchr", - "memmap2", + "memmap2 0.9.10", "thiserror 2.0.18", "tracing", "xdg", @@ -883,10 +1000,10 @@ dependencies = [ [[package]] name = "cosmic-protocols" version = "0.2.0" -source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#32283d76a8d0342da74c4cc022a533c52dcf378f" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", + "wayland-client", "wayland-protocols", "wayland-protocols-wlr", "wayland-scanner", @@ -895,8 +1012,7 @@ dependencies = [ [[package]] name = "cosmic-randr-shell" -version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-randr/#6e8e795970fa06d434af22775e415b517f7552d3" +version = "1.3.0" dependencies = [ "kdl", "slotmap", @@ -906,7 +1022,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", @@ -918,8 +1033,7 @@ dependencies = [ [[package]] name = "cosmic-settings-daemon" -version = "0.1.0" -source = "git+https://github.com/pop-os/dbus-settings-bindings#eed01dd3609e90e3c8cd043656734c500956c793" +version = "0.1.1-yoda.1" dependencies = [ "zbus", ] @@ -927,7 +1041,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", @@ -937,10 +1050,8 @@ dependencies = [ [[package]] name = "cosmic-text" version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be17b688510d934ce13f48a2beba700e11583e281e0fda99c22bb256a14eda73" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "fontdb", "harfrust", "linebender_resource_handle", @@ -956,12 +1067,12 @@ 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#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "almost", "configparser", @@ -1018,7 +1129,6 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cryoglyph" version = "0.1.0" -source = "git+https://github.com/iced-rs/cryoglyph.git?rev=e429a025df36ab8145708acb309080ae3deec17a#e429a025df36ab8145708acb309080ae3deec17a" dependencies = [ "cosmic-text", "etagere", @@ -1179,8 +1289,8 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.11.0", - "objc2", + "bitflags 2.13.2", + "objc2 0.6.4", ] [[package]] @@ -1206,9 +1316,8 @@ 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", + "bitflags 2.13.2", "mime 0.1.0", "raw-window-handle", "smithay-client-toolkit", @@ -1233,21 +1342,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" @@ -1255,31 +1355,21 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", - "drm-ffi 0.9.1", + "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.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51a91c9b32ac4e8105dec255e849e0d66e27d7c34d184364fb93e469db08f690" dependencies = [ - "drm-sys 0.8.1", + "drm-sys", "rustix 1.1.4", ] @@ -1289,16 +1379,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.1" @@ -1332,7 +1412,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25dd34cec49ab55d85ebf70139cb1ccd29c977ef6b6ba4fe85489d6877ee9ef3" dependencies = [ "ahash", - "bitflags 2.11.0", + "bitflags 2.13.2", "emath", "epaint", "nohash-hasher", @@ -1698,7 +1778,7 @@ checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" dependencies = [ "fontconfig-parser", "log", - "memmap2", + "memmap2 0.9.10", "slotmap", "tinyvec", "ttf-parser", @@ -1856,8 +1936,8 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce852e998d3ca5e4a97014fb31c940dc5ef344ec7d364984525fd11e8a547e6a" dependencies = [ - "bitflags 2.11.0", - "drm 0.14.1", + "bitflags 2.13.2", + "drm", "drm-fourcc", "gbm-sys", "libc", @@ -1883,8 +1963,8 @@ dependencies = [ "libc", "log", "rustversion", - "windows-link", - "windows-result", + "windows-link 0.2.1", + "windows-result 0.4.1", ] [[package]] @@ -1904,7 +1984,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]] @@ -1995,6 +2075,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.13.2", + "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.13.2", +] + [[package]] name = "grid" version = "1.0.0" @@ -2029,7 +2152,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f9f40651a03bc0f7316bd75267ff5767e93017ef3cfffe76c6aa7252cc5a31c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "core_maths", "read-fonts 0.37.0", @@ -2057,6 +2180,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", ] @@ -2204,7 +2329,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.62.2", ] [[package]] @@ -2219,9 +2344,10 @@ dependencies = [ [[package]] name = "iced" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ + "build_helpers", "dnd", + "iced_accessibility", "iced_core", "iced_debug", "iced_futures", @@ -2229,19 +2355,29 @@ 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#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", + "build_helpers", "bytes", + "cosmic-client-toolkit", "dnd", "glam 0.25.0", "lilt", @@ -2262,7 +2398,6 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "iced_core", "iced_futures", @@ -2272,7 +2407,6 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "futures", "iced_core", @@ -2285,9 +2419,8 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "cosmic-text", "half", @@ -2306,7 +2439,6 @@ dependencies = [ [[package]] name = "iced_program" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "iced_graphics", "iced_runtime", @@ -2315,7 +2447,6 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -2327,9 +2458,10 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ + "build_helpers", "bytes", + "cosmic-client-toolkit", "dnd", "iced_core", "iced_futures", @@ -2341,7 +2473,6 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "bytemuck", "cosmic-text", @@ -2352,17 +2483,17 @@ dependencies = [ "resvg 0.45.1", "rustc-hash 2.1.1", "softbuffer", - "tiny-skia", + "tiny-skia 0.11.4", ] [[package]] name = "iced_wgpu" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ - "as-raw-xcb-connection", - "bitflags 2.11.0", + "bitflags 2.13.2", + "build_helpers", "bytemuck", + "cosmic-client-toolkit", "cryoglyph", "futures", "glam 0.25.0", @@ -2376,20 +2507,19 @@ 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#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ + "build_helpers", + "cosmic-client-toolkit", "dnd", "iced_renderer", "iced_runtime", @@ -2402,6 +2532,38 @@ dependencies = [ "window_clipboard", ] +[[package]] +name = "iced_winit" +version = "0.14.0" +dependencies = [ + "build_helpers", + "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" @@ -2590,7 +2752,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "inotify-sys", "libc", ] @@ -2610,7 +2772,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9793345a65d71317763a33066b5d8351f8760dde8d4930fe9e39b5f14a7959d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "input-sys", "libc", "log", @@ -2768,7 +2930,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fbe853b403ae61a04233030ae8a79d94975281ed9770a1f9e246732b534b28d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "serde", ] @@ -2782,6 +2944,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" @@ -2866,12 +3039,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] -name = "libcosmic" -version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" +name = "libcosmic-yoda" +version = "0.1.0-yoda.2" dependencies = [ "apply", + "ashpd", "auto_enums", + "build_helpers", + "cosmic-client-toolkit", "cosmic-config", "cosmic-freedesktop-icons", "cosmic-settings-daemon", @@ -2889,7 +3064,9 @@ dependencies = [ "iced_renderer", "iced_runtime", "iced_tiny_skia", + "iced_wgpu", "iced_widget", + "iced_winit", "image", "jiff", "log", @@ -2912,7 +3089,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59fd96dbb2381ff31f314f07accbdf8550febdcc5cd8761ecaf7c1763361c359" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "libc", "libdisplay-info-derive", "libdisplay-info-sys", @@ -2947,7 +3124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -2958,13 +3135,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.12" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +checksum = "8d8f1ea3f21fd3405dcaf6c9b5c1630af9afc422d9073ea39c5f6d6c772e08ed" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "libc", - "redox_syscall 0.7.2", + "plain", + "redox_syscall 0.9.4", ] [[package]] @@ -3036,12 +3214,6 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" -[[package]] -name = "linux-raw-sys" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" - [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -3196,6 +3368,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" @@ -3214,6 +3395,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.13.2", + "block", + "core-graphics-types 0.2.0", + "foreign-types", + "log", + "objc", + "paste", +] + [[package]] name = "miette" version = "7.6.0" @@ -3221,13 +3417,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", ] @@ -3302,7 +3497,7 @@ checksum = "618f667225063219ddfc61251087db8a9aec3c3f0950c916b614e403486f1135" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.11.0", + "bitflags 2.13.2", "cfg-if", "cfg_aliases", "codespan-reporting", @@ -3315,6 +3510,7 @@ dependencies = [ "num-traits", "once_cell", "rustc-hash 1.1.0", + "spirv", "thiserror 2.0.18", "unicode-ident", ] @@ -3325,7 +3521,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "jni-sys", "log", "ndk-sys", @@ -3355,7 +3551,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "cfg-if", "cfg_aliases", "libc", @@ -3383,7 +3579,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "fsevent-sys", "inotify", "kqueue", @@ -3401,7 +3597,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", ] [[package]] @@ -3535,6 +3731,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" @@ -3544,16 +3756,44 @@ 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.13.2", + "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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags 2.11.0", - "objc2", + "bitflags 2.13.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.13.2", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -3562,10 +3802,10 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.11.0", - "block2", + "bitflags 2.13.2", + "block2 0.6.2", "dispatch2", - "objc2", + "objc2 0.6.4", ] [[package]] @@ -3574,18 +3814,30 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "libc", "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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "objc2-core-foundation", "objc2-core-graphics", ] @@ -3596,28 +3848,65 @@ 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.13.2", + "block2 0.5.1", + "libc", + "objc2 0.5.2", +] + [[package]] name = "objc2-foundation" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.11.0", - "block2", - "objc2", + "bitflags 2.13.2", + "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.13.2", + "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.13.2", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + [[package]] name = "objc2-ui-kit" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ - "bitflags 2.11.0", - "objc2", + "bitflags 2.13.2", + "objc2 0.6.4", "objc2-core-foundation", - "objc2-foundation", + "objc2-foundation 0.3.2", ] [[package]] @@ -3763,7 +4052,7 @@ dependencies = [ "libc", "redox_syscall 0.5.18", "smallvec", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -3940,6 +4229,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" @@ -3959,7 +4254,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "crc32fast", "fdeflate", "flate2", @@ -4019,6 +4314,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" @@ -4148,10 +4449,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", + "rand_chacha 0.3.1", "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + [[package]] name = "rand" version = "0.10.0" @@ -4173,6 +4484,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + [[package]] name = "rand_core" version = "0.6.4" @@ -4182,12 +4503,27 @@ dependencies = [ "getrandom 0.2.17", ] +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + [[package]] name = "rand_core" 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" @@ -4227,22 +4563,32 @@ 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.13.2", + "libredox", +] + [[package]] name = "redox_syscall" version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", ] [[package]] name = "redox_syscall" -version = "0.7.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d94dd2f7cd932d4dc02cc8b2b50dfd38bd079a4e5d79198b99743d7fcf9a4b4" +checksum = "737970939a87c6fa31e7acad13307bccbb017a073b695b6089a2c484f929e20e" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", ] [[package]] @@ -4333,7 +4679,7 @@ dependencies = [ "pico-args", "rgb", "svgtypes 0.13.0", - "tiny-skia", + "tiny-skia 0.11.4", "usvg 0.37.0", ] @@ -4349,7 +4695,7 @@ dependencies = [ "pico-args", "rgb", "svgtypes 0.15.3", - "tiny-skia", + "tiny-skia 0.11.4", "usvg 0.45.1", "zune-jpeg 0.4.21", ] @@ -4370,7 +4716,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db09040cc89e461f1a265139777a2bde7f8d8c67c4936f700c63ce3e2904d468" dependencies = [ "base64 0.22.1", - "bitflags 2.11.0", + "bitflags 2.13.2", "serde", "serde_derive", "unicode-ident", @@ -4382,7 +4728,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd490c5b18261893f14449cbd28cb9c0b637aebf161cd77900bfdedaff21ec32" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "once_cell", "serde", "serde_derive", @@ -4460,7 +4806,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "errno", "libc", "linux-raw-sys 0.4.15", @@ -4473,7 +4819,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "errno", "libc", "linux-raw-sys 0.12.1", @@ -4492,7 +4838,7 @@ version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "core_maths", "log", @@ -4558,6 +4904,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sctk-adwaita" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2a4360b9abcdcee43809e6fc11d6d61ca5d25734026a8fc46c2883eea3f13f" +dependencies = [ + "ab_glyph", + "log", + "memmap2 0.9.10", + "smithay-client-toolkit", + "tiny-skia 0.12.0", +] + [[package]] name = "self_cell" version = "1.2.2" @@ -4777,15 +5136,15 @@ dependencies = [ "aliasable", "appendlist", "ash", - "bitflags 2.11.0", + "bitflags 2.13.2", "calloop", "cc", "cursor-icon", "downcast-rs", - "drm 0.14.1", - "drm-ffi 0.9.1", + "drm", + "drm-ffi", "drm-fourcc", - "drm-sys 0.8.1", + "drm-sys", "encoding_rs", "errno", "gbm", @@ -4819,7 +5178,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", ] @@ -4830,13 +5189,15 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", + "bytemuck", "calloop", "calloop-wayland-source", "cursor-icon", "libc", "log", - "memmap2", + "memmap2 0.9.10", + "pkg-config", "rustix 1.1.4", "thiserror 2.0.18", "wayland-backend", @@ -4848,13 +5209,13 @@ dependencies = [ "wayland-protocols-misc", "wayland-protocols-wlr", "wayland-scanner", + "xkbcommon 0.8.0", "xkeysym", ] [[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", @@ -4895,22 +5256,20 @@ checksum = "27207bb65232eda1f588cf46db2fee75c0808d557f6b3cf19a75f5d6d7c94df1" [[package]] name = "softbuffer" version = "0.4.1" -source = "git+https://github.com/pop-os/softbuffer?tag=cosmic-4.0#c2b2c19ddb38ff17495643699f97cb1f2064a1be" dependencies = [ "as-raw-xcb-connection", "bytemuck", "cfg_aliases", "cocoa", "core-graphics", - "drm 0.11.1", "fastrand", "foreign-types", "js-sys", + "libredox", "log", - "memmap2", + "memmap2 0.9.10", "objc", "raw-window-handle", - "redox_syscall 0.5.18", "rustix 0.38.44", "tiny-xlib", "wasm-bindgen", @@ -4922,6 +5281,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.13.2", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -5067,6 +5435,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" @@ -5159,7 +5536,21 @@ dependencies = [ "cfg-if", "log", "png 0.17.16", - "tiny-skia-path", + "tiny-skia-path 0.11.4", +] + +[[package]] +name = "tiny-skia" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47ffee5eaaf5527f630fb0e356b90ebdec84d5d18d937c5e440350f88c5a91ea" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "tiny-skia-path 0.12.0", ] [[package]] @@ -5173,6 +5564,17 @@ dependencies = [ "strict-num", ] +[[package]] +name = "tiny-skia-path" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca365c3faccca67d06593c5980fa6c57687de727a03131735bb85f01fdeeb9" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + [[package]] name = "tiny-xlib" version = "0.2.4" @@ -5513,6 +5915,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" @@ -5529,6 +5937,7 @@ dependencies = [ "idna", "percent-encoding", "serde", + "serde_derive", ] [[package]] @@ -5565,7 +5974,7 @@ dependencies = [ "siphasher 1.0.2", "strict-num", "svgtypes 0.15.3", - "tiny-skia-path", + "tiny-skia-path 0.11.4", "unicode-bidi", "unicode-script", "unicode-vo", @@ -5599,7 +6008,7 @@ dependencies = [ "rctree", "strict-num", "svgtypes 0.13.0", - "tiny-skia-path", + "tiny-skia-path 0.11.4", ] [[package]] @@ -5758,7 +6167,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "hashbrown 0.15.5", "indexmap 2.13.0", "semver", @@ -5798,7 +6207,7 @@ version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "rustix 1.1.4", "wayland-backend", "wayland-scanner", @@ -5810,7 +6219,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "cursor-icon", "wayland-backend", ] @@ -5842,7 +6251,7 @@ version = "0.32.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", "wayland-scanner", @@ -5855,7 +6264,7 @@ version = "20250721.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5868,7 +6277,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5882,7 +6291,7 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa98634619300a535a9a97f338aed9a5ff1e01a461943e8346ff4ae26007306b" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5895,7 +6304,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5920,7 +6329,7 @@ version = "0.31.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc1846eb04c49182e04f4a099e2a830a2b745610bbc1d61246e206f29c7000a0" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "downcast-rs", "rustix 1.1.4", "wayland-backend", @@ -5972,18 +6381,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9cb534d5ffd109c7d1135f34cdae29e60eab94855a625dcfe1705f8bc7ad79f" dependencies = [ "arrayvec", - "bitflags 2.11.0", + "bitflags 2.13.2", "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", @@ -5998,7 +6413,7 @@ dependencies = [ "arrayvec", "bit-set", "bit-vec", - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "cfg_aliases", "document-features", @@ -6014,11 +6429,31 @@ 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-apple" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +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" @@ -6034,18 +6469,46 @@ version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d6cb474beb218824dcc9e1ce679d973f719262789bfb27407da560cac20eeb" dependencies = [ - "bitflags 2.11.0", + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags 2.13.2", + "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]] @@ -6054,7 +6517,7 @@ version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e18308757e594ed2cd27dddbb16a139c42a683819d32a2e0b1b0167552f5840c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "js-sys", "log", @@ -6095,18 +6558,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" @@ -6115,9 +6632,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]] @@ -6142,19 +6681,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]] @@ -6163,7 +6746,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]] @@ -6217,7 +6800,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]] @@ -6272,7 +6855,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", @@ -6283,6 +6866,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" @@ -6466,30 +7067,68 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winit" version = "0.31.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2879d2854d1a43e48f67322d4bd097afcb6eb8f8f775c8de0260a71aea1df1aa" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "cfg_aliases", "cursor-icon", - "dpi", + "dpi 0.1.2", "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", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2879d2854d1a43e48f67322d4bd097afcb6eb8f8f775c8de0260a71aea1df1aa" +dependencies = [ + "bitflags 2.13.2", + "cfg_aliases", + "cursor-icon", + "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 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.13.2", + "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" @@ -6497,13 +7136,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d9c0d2cd93efec3a9f9ad819cfaf0834782403af7c0d248c784ec0c61761df" dependencies = [ "android-activity", - "bitflags 2.11.0", - "dpi", + "bitflags 2.13.2", + "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.13.2", + "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]] @@ -6512,21 +7172,34 @@ version = "0.31.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21310ca07851a49c348e0c2cc768e36b52ca65afda2c2354d78ed4b90074d8aa" dependencies = [ - "bitflags 2.11.0", - "block2", + "bitflags 2.13.2", + "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]] @@ -6535,45 +7208,92 @@ 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.13.2", + "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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4f0ccd7abb43740e2c6124ac7cae7d865ecec74eec63783e8922577ac232583" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "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.13.2", + "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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51ea1fb262e7209f265f12bd0cc792c399b14355675e65531e9c8a87db287d46" dependencies = [ - "bitflags 2.11.0", - "dpi", + "bitflags 2.13.2", + "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.13.2", + "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]] @@ -6582,19 +7302,44 @@ version = "0.31.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "680a356e798837d8eb274d4556e83bceaf81698194e31aafc5cfb8a9f2fab643" dependencies = [ - "bitflags 2.11.0", - "block2", + "bitflags 2.13.2", + "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.13.2", + "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]] @@ -6604,12 +7349,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce5afb2ba07da603f84b722c95f9f9396d2cedae3944fb6c0cda4a6f88de545" dependencies = [ "ahash", - "bitflags 2.11.0", + "bitflags 2.13.2", "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", @@ -6619,21 +7364,19 @@ 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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2490a953fb776fbbd5e295d54f1c3847f4f15b6c3929ec53c09acda6487a92" dependencies = [ "atomic-waker", - "bitflags 2.11.0", + "bitflags 2.13.2", "concurrent-queue", "cursor-icon", - "dpi", + "dpi 0.1.2", "js-sys", "pin-project", "raw-window-handle", @@ -6643,7 +7386,45 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "web-time", - "winit-core", + "winit-core 0.31.0-beta.2", +] + +[[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.13.2", + "concurrent-queue", + "cursor-icon", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "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 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-win32" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.13.2", + "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]] @@ -6652,15 +7433,15 @@ version = "0.31.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "644ea78af0e858aa3b092e5d1c67c41995a98220c81813f1353b28bc8bb91eaa" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "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]] @@ -6669,19 +7450,19 @@ version = "0.31.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa5b600756534c7041aa93cd0d244d44b09fca1b89e202bd1cd80dd9f3636c46" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "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", @@ -6772,7 +7553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.11.0", + "bitflags 2.13.2", "indexmap 2.13.0", "log", "serde", @@ -6863,6 +7644,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" @@ -6870,7 +7662,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d66ca9352cbd4eecbbc40871d8a11b4ac8107cfc528a6e14d7c19c69d0e1ac9" dependencies = [ "libc", - "memmap2", + "memmap2 0.9.10", "xkeysym", ] @@ -6881,7 +7673,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a974f48060a14e95705c01f24ad9c3345022f4d97441b8a36beb7ed5c4a02d" dependencies = [ "libc", - "memmap2", + "memmap2 0.9.10", "xkeysym", ] @@ -6891,7 +7683,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "dlib", "log", "once_cell", @@ -6903,6 +7695,9 @@ name = "xkeysym" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" +dependencies = [ + "bytemuck", +] [[package]] name = "xml-rs" @@ -7138,6 +7933,7 @@ dependencies = [ "endi", "enumflags2", "serde", + "url", "winnow 1.0.3", "zvariant_derive", "zvariant_utils", @@ -7168,3 +7964,7 @@ dependencies = [ "syn", "winnow 1.0.3", ] + +[[patch.unused]] +name = "winit-x11" +version = "0.31.0-beta.2" From f9e02f4321f11998aa91ab86409692d0999aa626 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 8 Sep 2026 15:20:10 -0700 Subject: [PATCH 72/80] Add comments to functions that iterate over surfaces These should be kept in sync with `render_input_order_internal`. --- src/shell/focus/order.rs | 2 ++ src/shell/mod.rs | 2 ++ src/state.rs | 6 ++++++ 3 files changed, 10 insertions(+) diff --git a/src/shell/focus/order.rs b/src/shell/focus/order.rs index fb904c01..b439ba89 100644 --- a/src/shell/focus/order.rs +++ b/src/shell/focus/order.rs @@ -77,6 +77,8 @@ fn render_input_order_internal( element_filter: ElementFilter, mut callback: impl FnMut(Stage) -> ControlFlow, ()>, ) -> ControlFlow, ()> { + // NOTE: Keep in sync with other surface iteration functions + if shell .zoom_state .as_ref() diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 47d78851..63186afd 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -2046,6 +2046,8 @@ impl Shell { } pub fn visible_output_for_surface(&self, surface: &WlSurface) -> Option<&Output> { + // NOTE: Keep in sync with surface iteration in `render_input_order_internal` + if let Some(session_lock) = &self.session_lock { return session_lock .surfaces diff --git a/src/state.rs b/src/state.rs index 28ed8fd4..5c1109f8 100644 --- a/src/state.rs +++ b/src/state.rs @@ -968,6 +968,8 @@ impl Common { output: &Output, render_element_states: &RenderElementStates, ) { + // NOTE: Keep in sync with surface iteration in `render_input_order_internal` + let shell = self.shell.read(); let processor = |namespace: Option| { move |surface: &WlSurface, states: &SurfaceData| { @@ -1074,6 +1076,8 @@ impl Common { render_element_states: &RenderElementStates, mut dmabuf_feedback: impl FnMut(DrmNode) -> Option, ) { + // NOTE: Keep in sync with surface iteration in `render_input_order_internal` + let shell = self.shell.read(); if let Some(session_lock) = shell.session_lock.as_ref() @@ -1287,6 +1291,8 @@ impl Common { #[profiling::function] pub fn send_frames(&self, output: &Output, sequence: Option) { + // NOTE: Keep in sync with surface iteration in `render_input_order_internal` + let time = self.clock.now(); let should_send = |surface: &WlSurface, states: &SurfaceData| { // Do the standard primary scanout output check. For pointer surfaces it deduplicates From a7349b72e39fd91d7ed25eddb2fa24b900a615d5 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Fri, 28 Aug 2026 13:14:21 -0700 Subject: [PATCH 73/80] Add `cosmic-session-lock-layer-v1` protocol https://github.com/pop-os/cosmic-protocols/pull/88 --- Cargo.lock | 154 ++++++++++---------- src/shell/focus/mod.rs | 16 +- src/shell/focus/order.rs | 89 ++++++++++- src/shell/mod.rs | 22 ++- src/state.rs | 6 + src/wayland/protocols/mod.rs | 1 + src/wayland/protocols/session_lock_layer.rs | 136 +++++++++++++++++ 7 files changed, 339 insertions(+), 85 deletions(-) create mode 100644 src/wayland/protocols/session_lock_layer.rs diff --git a/Cargo.lock b/Cargo.lock index d0a95294..93ad9171 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,7 +74,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.11.0", + "bitflags 2.13.2", "cc", "cesu8", "jni", @@ -390,9 +390,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" dependencies = [ "serde_core", ] @@ -514,7 +514,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" dependencies = [ "async-task", - "bitflags 2.11.0", + "bitflags 2.13.2", "futures-core", "polling", "rustix 1.1.4", @@ -765,7 +765,7 @@ name = "cosmic-comp" version = "1.0.0" dependencies = [ "anyhow", - "bitflags 2.11.0", + "bitflags 2.13.2", "calloop", "clap_lex", "cosmic-comp-config", @@ -883,9 +883,9 @@ dependencies = [ [[package]] name = "cosmic-protocols" version = "0.2.0" -source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#32283d76a8d0342da74c4cc022a533c52dcf378f" +source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#c0cff4db14c37ed954983158e4055aa94c7741d9" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-protocols", "wayland-protocols-wlr", @@ -940,7 +940,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be17b688510d934ce13f48a2beba700e11583e281e0fda99c22bb256a14eda73" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "fontdb", "harfrust", "linebender_resource_handle", @@ -1170,7 +1170,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1179,7 +1179,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "objc2", ] @@ -1208,7 +1208,7 @@ 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", + "bitflags 2.13.2", "mime 0.1.0", "raw-window-handle", "smithay-client-toolkit", @@ -1242,7 +1242,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "drm-ffi 0.7.1", "drm-fourcc", @@ -1255,7 +1255,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "drm-ffi 0.9.1", "drm-fourcc", @@ -1332,7 +1332,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25dd34cec49ab55d85ebf70139cb1ccd29c977ef6b6ba4fe85489d6877ee9ef3" dependencies = [ "ahash", - "bitflags 2.11.0", + "bitflags 2.13.2", "emath", "epaint", "nohash-hasher", @@ -1484,7 +1484,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1856,7 +1856,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce852e998d3ca5e4a97014fb31c940dc5ef344ec7d364984525fd11e8a547e6a" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "drm 0.14.1", "drm-fourcc", "gbm-sys", @@ -2029,7 +2029,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f9f40651a03bc0f7316bd75267ff5767e93017ef3cfffe76c6aa7252cc5a31c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "core_maths", "read-fonts 0.37.0", @@ -2240,7 +2240,7 @@ name = "iced_core" version = "0.14.0" source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytes", "dnd", "glam 0.25.0", @@ -2287,7 +2287,7 @@ name = "iced_graphics" version = "0.14.0" source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "cosmic-text", "half", @@ -2361,7 +2361,7 @@ version = "0.14.0" source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "as-raw-xcb-connection", - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "cryoglyph", "futures", @@ -2590,7 +2590,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "inotify-sys", "libc", ] @@ -2610,7 +2610,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9793345a65d71317763a33066b5d8351f8760dde8d4930fe9e39b5f14a7959d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "input-sys", "libc", "log", @@ -2671,7 +2671,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2768,7 +2768,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fbe853b403ae61a04233030ae8a79d94975281ed9770a1f9e246732b534b28d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "serde", ] @@ -2794,7 +2794,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1886916523694cd6ea3d175f03a1e5010699a2a4cc13696d83d7bea1d80638" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -2912,7 +2912,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59fd96dbb2381ff31f314f07accbdf8550febdcc5cd8761ecaf7c1763361c359" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "libc", "libdisplay-info-derive", "libdisplay-info-sys", @@ -2962,7 +2962,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "libc", "redox_syscall 0.7.2", ] @@ -3302,7 +3302,7 @@ checksum = "618f667225063219ddfc61251087db8a9aec3c3f0950c916b614e403486f1135" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.11.0", + "bitflags 2.13.2", "cfg-if", "cfg_aliases", "codespan-reporting", @@ -3325,7 +3325,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "jni-sys", "log", "ndk-sys", @@ -3355,7 +3355,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "cfg-if", "cfg_aliases", "libc", @@ -3383,7 +3383,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "fsevent-sys", "inotify", "kqueue", @@ -3401,7 +3401,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", ] [[package]] @@ -3410,7 +3410,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3550,7 +3550,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "objc2", "objc2-core-foundation", "objc2-foundation", @@ -3562,7 +3562,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2", "dispatch2", "objc2", @@ -3574,7 +3574,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "libc", "objc2-core-foundation", ] @@ -3585,7 +3585,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "objc2-core-foundation", "objc2-core-graphics", ] @@ -3602,7 +3602,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2", "objc2", "objc2-core-foundation", @@ -3614,7 +3614,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "objc2", "objc2-core-foundation", "objc2-foundation", @@ -3959,7 +3959,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "crc32fast", "fdeflate", "flate2", @@ -4233,7 +4233,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", ] [[package]] @@ -4242,7 +4242,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d94dd2f7cd932d4dc02cc8b2b50dfd38bd079a4e5d79198b99743d7fcf9a4b4" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", ] [[package]] @@ -4370,7 +4370,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db09040cc89e461f1a265139777a2bde7f8d8c67c4936f700c63ce3e2904d468" dependencies = [ "base64 0.22.1", - "bitflags 2.11.0", + "bitflags 2.13.2", "serde", "serde_derive", "unicode-ident", @@ -4382,7 +4382,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd490c5b18261893f14449cbd28cb9c0b637aebf161cd77900bfdedaff21ec32" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "once_cell", "serde", "serde_derive", @@ -4460,7 +4460,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "errno", "libc", "linux-raw-sys 0.4.15", @@ -4473,11 +4473,11 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4492,7 +4492,7 @@ version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "core_maths", "log", @@ -4777,7 +4777,7 @@ dependencies = [ "aliasable", "appendlist", "ash", - "bitflags 2.11.0", + "bitflags 2.13.2", "calloop", "cc", "cursor-icon", @@ -4830,7 +4830,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "calloop", "calloop-wayland-source", "cursor-icon", @@ -5064,7 +5064,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5758,7 +5758,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "hashbrown 0.15.5", "indexmap 2.13.0", "semver", @@ -5798,7 +5798,7 @@ version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "rustix 1.1.4", "wayland-backend", "wayland-scanner", @@ -5810,7 +5810,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "cursor-icon", "wayland-backend", ] @@ -5842,7 +5842,7 @@ version = "0.32.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", "wayland-scanner", @@ -5855,7 +5855,7 @@ version = "20250721.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5868,7 +5868,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5882,7 +5882,7 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa98634619300a535a9a97f338aed9a5ff1e01a461943e8346ff4ae26007306b" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5895,7 +5895,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5920,7 +5920,7 @@ version = "0.31.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc1846eb04c49182e04f4a099e2a830a2b745610bbc1d61246e206f29c7000a0" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "downcast-rs", "rustix 1.1.4", "wayland-backend", @@ -5972,7 +5972,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9cb534d5ffd109c7d1135f34cdae29e60eab94855a625dcfe1705f8bc7ad79f" dependencies = [ "arrayvec", - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "cfg-if", "cfg_aliases", @@ -5998,7 +5998,7 @@ dependencies = [ "arrayvec", "bit-set", "bit-vec", - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "cfg_aliases", "document-features", @@ -6034,7 +6034,7 @@ version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d6cb474beb218824dcc9e1ce679d973f719262789bfb27407da560cac20eeb" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "cfg-if", "cfg_aliases", "libloading", @@ -6054,7 +6054,7 @@ version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e18308757e594ed2cd27dddbb16a139c42a683819d32a2e0b1b0167552f5840c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "js-sys", "log", @@ -6083,7 +6083,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] @@ -6469,7 +6469,7 @@ version = "0.31.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2879d2854d1a43e48f67322d4bd097afcb6eb8f8f775c8de0260a71aea1df1aa" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "cfg_aliases", "cursor-icon", "dpi", @@ -6497,7 +6497,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d9c0d2cd93efec3a9f9ad819cfaf0834782403af7c0d248c784ec0c61761df" dependencies = [ "android-activity", - "bitflags 2.11.0", + "bitflags 2.13.2", "dpi", "ndk", "raw-window-handle", @@ -6512,7 +6512,7 @@ version = "0.31.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21310ca07851a49c348e0c2cc768e36b52ca65afda2c2354d78ed4b90074d8aa" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2", "dispatch2", "dpi", @@ -6551,7 +6551,7 @@ version = "0.31.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4f0ccd7abb43740e2c6124ac7cae7d865ecec74eec63783e8922577ac232583" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "cursor-icon", "dpi", "keyboard-types", @@ -6566,7 +6566,7 @@ version = "0.31.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51ea1fb262e7209f265f12bd0cc792c399b14355675e65531e9c8a87db287d46" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "dpi", "orbclient", "raw-window-handle", @@ -6582,7 +6582,7 @@ version = "0.31.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "680a356e798837d8eb274d4556e83bceaf81698194e31aafc5cfb8a9f2fab643" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2", "dispatch2", "dpi", @@ -6604,7 +6604,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce5afb2ba07da603f84b722c95f9f9396d2cedae3944fb6c0cda4a6f88de545" dependencies = [ "ahash", - "bitflags 2.11.0", + "bitflags 2.13.2", "calloop", "cursor-icon", "dpi", @@ -6630,7 +6630,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c2490a953fb776fbbd5e295d54f1c3847f4f15b6c3929ec53c09acda6487a92" dependencies = [ "atomic-waker", - "bitflags 2.11.0", + "bitflags 2.13.2", "concurrent-queue", "cursor-icon", "dpi", @@ -6652,7 +6652,7 @@ version = "0.31.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "644ea78af0e858aa3b092e5d1c67c41995a98220c81813f1353b28bc8bb91eaa" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "cursor-icon", "dpi", "raw-window-handle", @@ -6669,7 +6669,7 @@ version = "0.31.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa5b600756534c7041aa93cd0d244d44b09fca1b89e202bd1cd80dd9f3636c46" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "calloop", "cursor-icon", @@ -6772,7 +6772,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.11.0", + "bitflags 2.13.2", "indexmap 2.13.0", "log", "serde", @@ -6891,7 +6891,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "dlib", "log", "once_cell", diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index f631372e..fe8f772a 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -2,12 +2,15 @@ use crate::{ shell::{CosmicSurface, MinimizedWindow, Shell, Trigger, element::CosmicMapped}, state::{Common, State}, utils::prelude::*, - wayland::handlers::{xdg_shell::PopupGrabData, xwayland_keyboard_grab::XWaylandGrabSeatData}, + wayland::{ + handlers::{xdg_shell::PopupGrabData, xwayland_keyboard_grab::XWaylandGrabSeatData}, + protocols::session_lock_layer::layer_show_on_lock, + }, }; use indexmap::IndexSet; use smithay::{ backend::input::InputTime, - desktop::{PopupUngrabStrategy, layer_map_for_output}, + desktop::{PopupUngrabStrategy, find_popup_root_surface, layer_map_for_output}, input::{Seat, pointer::MotionEvent}, output::Output, reexports::wayland_server::{Resource, protocol::wl_surface::WlSurface}, @@ -627,8 +630,15 @@ fn focus_target_is_valid( output: &Output, target: KeyboardFocusTarget, ) -> bool { - // If a session lock is active, only lock surfaces can be focused + // If a session lock is active, only lock surfaces and lock layers can be focused if shell.session_lock.is_some() { + if let KeyboardFocusTarget::LayerSurface(layer) = &target { + return layer_show_on_lock(layer.wl_surface()); + } else if let KeyboardFocusTarget::Popup(popup) = &target + && let Ok(root) = find_popup_root_surface(popup) + { + return layer_show_on_lock(&root); + } return matches!(target, KeyboardFocusTarget::LockSurface(_)); } diff --git a/src/shell/focus/order.rs b/src/shell/focus/order.rs index b439ba89..937fa311 100644 --- a/src/shell/focus/order.rs +++ b/src/shell/focus/order.rs @@ -22,7 +22,7 @@ use crate::{ prelude::OutputExt, quirks::{WORKSPACE_OVERVIEW_NAMESPACE, workspace_overview_is_open}, }, - wayland::protocols::workspace::WorkspaceHandle, + wayland::protocols::{session_lock_layer::layer_show_on_lock, workspace::WorkspaceHandle}, }; pub enum Stage<'a> { @@ -89,7 +89,92 @@ fn render_input_order_internal( // Session Lock if let Some(session_lock) = &shell.session_lock { - return callback(Stage::SessionLock(session_lock.surfaces.get(output))); + for (layer, popup, location) in layer_popups(output, Layer::Overlay, element_filter) { + if !layer_show_on_lock(layer.wl_surface()) { + continue; + } + callback(Stage::LayerPopup { + layer, + popup: &popup, + location, + workspace_idx: current.1, + })?; + } + for (layer, location) in layer_surfaces(output, Layer::Overlay, element_filter) { + if !layer_show_on_lock(layer.wl_surface()) { + continue; + } + callback(Stage::LayerSurface { + layer, + location, + workspace_idx: current.1, + })?; + } + for (layer, popup, location) in layer_popups(output, Layer::Top, element_filter) { + if !layer_show_on_lock(layer.wl_surface()) { + continue; + } + callback(Stage::LayerPopup { + layer, + popup: &popup, + location, + workspace_idx: current.1, + })?; + } + for (layer, location) in layer_surfaces(output, Layer::Top, element_filter) { + if !layer_show_on_lock(layer.wl_surface()) { + continue; + } + callback(Stage::LayerSurface { + layer, + location, + workspace_idx: current.1, + })?; + } + callback(Stage::SessionLock(session_lock.surfaces.get(output)))?; + for (layer, popup, location) in layer_popups(output, Layer::Bottom, element_filter) { + if !layer_show_on_lock(layer.wl_surface()) { + continue; + } + callback(Stage::LayerPopup { + layer, + popup: &popup, + location, + workspace_idx: current.1, + })?; + } + for (layer, popup, location) in layer_popups(output, Layer::Background, element_filter) { + if !layer_show_on_lock(layer.wl_surface()) { + continue; + } + callback(Stage::LayerPopup { + layer, + popup: &popup, + location, + workspace_idx: current.1, + })?; + } + for (layer, location) in layer_surfaces(output, Layer::Bottom, element_filter) { + if !layer_show_on_lock(layer.wl_surface()) { + continue; + } + callback(Stage::LayerSurface { + layer, + location, + workspace_idx: current.1, + })?; + } + for (layer, location) in layer_surfaces(output, Layer::Background, element_filter) { + if !layer_show_on_lock(layer.wl_surface()) { + continue; + } + callback(Stage::LayerSurface { + layer, + location, + workspace_idx: current.1, + })?; + } + return ControlFlow::Continue(()); } // Overlay-level layer shell diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 63186afd..8439b13f 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -19,7 +19,10 @@ use crate::{ utils, wayland::{ handlers::data_device::{self, get_dnd_icon}, - protocols::workspace::{State as WState, WorkspaceCapabilities}, + protocols::{ + session_lock_layer::layer_show_on_lock, + workspace::{State as WState, WorkspaceCapabilities}, + }, }, }; use cosmic_comp_config::{ @@ -2049,11 +2052,24 @@ impl Shell { // NOTE: Keep in sync with surface iteration in `render_input_order_internal` if let Some(session_lock) = &self.session_lock { - return session_lock + if let Some((output, _)) = session_lock .surfaces .iter() .find(|(_, v)| v.wl_surface() == surface) - .map(|(k, _)| k); + { + return Some(output); + } + for o in self.outputs() { + let map = layer_map_for_output(o); + if let Some(layer_surface) = map.layer_for_surface(surface, WindowSurfaceType::ALL) + { + if layer_show_on_lock(layer_surface.wl_surface()) { + return Some(o); + } else { + return None; + } + } + } } self.outputs() diff --git a/src/state.rs b/src/state.rs index 5c1109f8..d605c736 100644 --- a/src/state.rs +++ b/src/state.rs @@ -23,6 +23,7 @@ use crate::{ output_configuration::OutputConfigurationState, output_power::OutputPowerState, overlap_notify::OverlapNotifyState, + session_lock_layer::SessionLockLayerState, toplevel_info::ToplevelInfoState, toplevel_management::{ManagementCapabilities, ToplevelManagementState}, workspace::{WorkspaceState, WorkspaceUpdateGuard}, @@ -320,6 +321,7 @@ pub struct Common { pub xwayland_state: Option, pub xwayland_shell_state: XWaylandShellState, pub pointer_focus_state: Option, + pub session_lock_layer_state: SessionLockLayerState, #[cfg(feature = "logind")] pub inhibit_lid_fd: Option, @@ -763,6 +765,9 @@ impl State { let dbus_state = DBusState::init(&handle); + let session_lock_layer_state = + SessionLockLayerState::new::(dh, client_not_sandboxed); + State { common: Common { config, @@ -830,6 +835,7 @@ impl State { pointer_focus_state: None, dbus_state, keyboard_layout_state, + session_lock_layer_state, #[cfg(feature = "logind")] inhibit_lid_fd: None, diff --git a/src/wayland/protocols/mod.rs b/src/wayland/protocols/mod.rs index c6c0234a..48aa8337 100644 --- a/src/wayland/protocols/mod.rs +++ b/src/wayland/protocols/mod.rs @@ -8,6 +8,7 @@ pub mod keyboard_layout; pub mod output_configuration; pub mod output_power; pub mod overlap_notify; +pub mod session_lock_layer; pub mod toplevel_info; pub mod toplevel_management; pub mod workspace; diff --git a/src/wayland/protocols/session_lock_layer.rs b/src/wayland/protocols/session_lock_layer.rs new file mode 100644 index 00000000..85edda50 --- /dev/null +++ b/src/wayland/protocols/session_lock_layer.rs @@ -0,0 +1,136 @@ +use cosmic_protocols::session_lock_layer::v1::server::cosmic_session_lock_layer_manager_v1; +use smithay::{ + reexports::wayland_server::{ + Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, backend::GlobalId, + protocol::wl_surface, + }, + wayland::{ + Dispatch2, GlobalDispatch2, + compositor::{Cacheable, with_states}, + shell::wlr_layer::WlrLayerShellHandler, + }, +}; + +struct SessionLockLayerData; + +#[derive(Debug)] +pub struct SessionLockLayerState { + global: GlobalId, +} + +impl SessionLockLayerState { + pub fn new(dh: &DisplayHandle, client_filter: F) -> Self + where + F: for<'a> Fn(&'a Client) -> bool + Clone + Send + Sync + 'static, + D: GlobalDispatch< + cosmic_session_lock_layer_manager_v1::CosmicSessionLockLayerManagerV1, + SessionLockLayerGlobalData, + > + 'static, + { + let global = dh + .create_global::( + 1, + SessionLockLayerGlobalData { + filter: Box::new(client_filter.clone()), + }, + ); + Self { global } + } + + pub fn global_id(&self) -> GlobalId { + self.global.clone() + } +} + +#[doc(hidden)] +pub struct SessionLockLayerGlobalData { + filter: Box Fn(&'a Client) -> bool + Send + Sync>, +} + +impl GlobalDispatch2 + for SessionLockLayerGlobalData +where + D: Dispatch< + cosmic_session_lock_layer_manager_v1::CosmicSessionLockLayerManagerV1, + SessionLockLayerData, + >, +{ + fn bind( + &self, + _state: &mut D, + _handle: &DisplayHandle, + _client: &Client, + resource: New, + data_init: &mut DataInit<'_, D>, + ) { + data_init.init(resource, SessionLockLayerData); + } + + fn can_view(&self, client: &Client) -> bool { + (self.filter)(client) + } +} + +#[derive(Clone, Copy, Default)] +struct SessionLockLayerSurfaceData { + show_on_lock: bool, +} + +impl Cacheable for SessionLockLayerSurfaceData { + fn commit(&mut self, _dh: &DisplayHandle) -> Self { + *self + } + + fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) { + *into = self; + } +} + +pub fn layer_show_on_lock(wl_surface: &wl_surface::WlSurface) -> bool { + with_states(wl_surface, |states| { + if states.cached_state.has::() { + let mut state = states.cached_state.get::(); + state.current().show_on_lock + } else { + false + } + }) +} + +impl Dispatch2 + for SessionLockLayerData +where + D: WlrLayerShellHandler, +{ + fn request( + &self, + state: &mut D, + _: &Client, + _: &cosmic_session_lock_layer_manager_v1::CosmicSessionLockLayerManagerV1, + request: cosmic_session_lock_layer_manager_v1::Request, + _: &DisplayHandle, + _: &mut DataInit<'_, D>, + ) { + let (layer, value) = match request { + cosmic_session_lock_layer_manager_v1::Request::SetShowOnLock { layer } => (layer, true), + cosmic_session_lock_layer_manager_v1::Request::UnsetShowOnLock { layer } => { + (layer, false) + } + cosmic_session_lock_layer_manager_v1::Request::Destroy => { + return; + } + _ => unreachable!(), + }; + + if let Some(layer) = state + .shell_state() + .layer_surfaces() + .find(|surface| surface.shell_surface() == &layer) + { + with_states(layer.wl_surface(), |states| { + let mut state = states.cached_state.get::(); + state.pending().show_on_lock = value; + }); + } + } +} From 651f701f84e46d0262a129cb9ac30537d648cb08 Mon Sep 17 00:00:00 2001 From: Hojjat Date: Thu, 10 Sep 2026 15:08:32 -0600 Subject: [PATCH 74/80] shell/focus: Don't dismiss grabbing popups of exclusive layer surfaces --- src/shell/focus/mod.rs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index fe8f772a..dcfd55de 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -10,7 +10,10 @@ use crate::{ use indexmap::IndexSet; use smithay::{ backend::input::InputTime, - desktop::{PopupUngrabStrategy, find_popup_root_surface, layer_map_for_output}, + desktop::{ + LayerSurface, PopupUngrabStrategy, WindowSurfaceType, find_popup_root_surface, + layer_map_for_output, + }, input::{Seat, pointer::MotionEvent}, output::Output, reexports::wayland_server::{Resource, protocol::wl_surface::WlSurface}, @@ -644,12 +647,28 @@ fn focus_target_is_valid( // If an exclusive layer shell surface exists (on any output), only exclusive // shell surfaces can have focus, on the highest layer with exclusive surfaces. + // Popups are judged by their root surface, so an exclusive surface can + // still open grabbing popups (menus, dropdowns, context menus). if let Some(layer) = exclusive_layer_surface_layer(shell) { - return if let KeyboardFocusTarget::LayerSurface(layer_surface) = target { + let is_exclusive_on_layer = |layer_surface: &LayerSurface| { let data = layer_surface.cached_state(); (data.keyboard_interactivity, data.layer) == (KeyboardInteractivity::Exclusive, layer) - } else { - false + }; + return match target { + KeyboardFocusTarget::LayerSurface(layer_surface) => { + is_exclusive_on_layer(&layer_surface) + } + KeyboardFocusTarget::Popup(popup) => find_popup_root_surface(&popup) + .ok() + .and_then(|root| { + shell.outputs().find_map(|o| { + layer_map_for_output(o) + .layer_for_surface(&root, WindowSurfaceType::ALL) + .map(&is_exclusive_on_layer) + }) + }) + .unwrap_or(false), + _ => false, }; } From cd881a4df28f99bf1fefc5823f755ee646b9e12a Mon Sep 17 00:00:00 2001 From: Flori Ava Star Weber Date: Fri, 18 Sep 2026 17:09:07 +0200 Subject: [PATCH 75/80] cli: change incorrect text `-v` to `-V` --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index aa31a464..98bfa2a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -289,7 +289,7 @@ Project home page: https://github.com/pop-os/cosmic-comp Options: -h, --help Show this message --no-xwayland Run without Xwayland - -v, --version Show the version of cosmic-comp"# + -V, --version Show the version of cosmic-comp"# ); } From 26daf75d93695d3ecd9418f4cfe8e0564ddc53db Mon Sep 17 00:00:00 2001 From: Hojjat Date: Thu, 10 Sep 2026 17:43:41 -0600 Subject: [PATCH 76/80] fix: forward release for modifier-only bindings --- src/input/mod.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/input/mod.rs b/src/input/mod.rs index 45586b03..c48fea8d 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -2096,6 +2096,21 @@ impl State { backend_id, seat, modifiers, handle, serial, keycode, key_state, time, ); + // A modifier-only binding fires on the modifier's release, but its press was already + // forwarded. Forward the release too. + if key_state == KeyState::Released + && matches!(&result, FilterResult::Intercept(Some((_, binding))) if binding.key.is_none()) + { + seat.get_keyboard().unwrap().input_forward( + self, + keycode, + key_state, + serial, + time, + previous_modifiers != *modifiers, + ); + } + if (matches!(result, FilterResult::Forward) && !seat.get_keyboard().unwrap().is_grabbed() && !shortcuts_inhibited From 50c170c355386f91415b7579b1342ad2e4a7f226 Mon Sep 17 00:00:00 2001 From: Hojjat Date: Tue, 15 Sep 2026 17:35:18 -0600 Subject: [PATCH 77/80] fix: only cancel grabs the compositor started for its own interactions --- src/input/mod.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index c48fea8d..af160ae2 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -19,7 +19,7 @@ use crate::{ Stage, render_input_order, target::{KeyboardFocusTarget, PointerFocusTarget}, }, - grabs::{ReleaseMode, ResizeEdge}, + grabs::{MenuGrab, MoveGrab, ReleaseMode, ResizeEdge, ResizeGrab}, layout::{ floating::ResizeGrabMarker, tiling::{NodeDesc, SwapWindowGrab, TilingLayout}, @@ -49,7 +49,7 @@ use smithay::{ PointerAxisEvent, ProximityState, TabletToolButtonEvent, TabletToolEvent, TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState, TouchEvent, }, - desktop::{PopupKeyboardGrab, WindowSurfaceType, utils::under_from_surface_tree}, + desktop::{WindowSurfaceType, utils::under_from_surface_tree}, input::{ Seat, keyboard::KeyboardHandle, @@ -2306,17 +2306,15 @@ impl State { let keyboard = seat.get_keyboard().unwrap(); let pointer = seat.get_pointer().unwrap(); - // We're only interested in filtering keyboard grabs if we initiated them. - // The easiest way to check that is to check the type of the grab. - let keyboard_grabbed = keyboard.with_grab(|_serial, grab| { - grab.is::() || grab.is::>() + // Escape only cancels grabs the compositor started for its own interactions + // For keyboard: SwapWindowGrab + // For pointer: MoveGrab, ResizeGrab, and MenuGrab + let keyboard_grabbed = + keyboard.with_grab(|_serial, grab| grab.is::()) == Some(true); + let pointer_grabbed = pointer.with_grab(|_serial, grab| { + grab.is::() || grab.is::() || grab.is::() }) == Some(true); - // A virtual-keyboard key can arrive while the seat's pointer is grabbed by that - // same on-screen keyboard's own button press (the implicit grab from clicking an OSK - // key). That pointer grab must not capture the injected key, otherwise e.g. - // pressing esc on a virtual keyboard gets swallowed here - let from_vk = matches!(backend_id, InputBackendId::VirtualKeyboard); - let is_grabbed = keyboard_grabbed || (pointer.is_grabbed() && !from_vk); + let is_grabbed = keyboard_grabbed || pointer_grabbed; let current_focus = keyboard.current_focus(); //this should fall back to active output since there may not be a focused output From d36480c1b5e39869259e6477fe8bc42bf900254f Mon Sep 17 00:00:00 2001 From: Hojjat Date: Thu, 17 Sep 2026 12:41:15 -0600 Subject: [PATCH 78/80] fix: only unmaximize on activation if `maximized != target` --- src/wayland/handlers/xdg_activation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wayland/handlers/xdg_activation.rs b/src/wayland/handlers/xdg_activation.rs index ad8ce0d0..38ba6219 100644 --- a/src/wayland/handlers/xdg_activation.rs +++ b/src/wayland/handlers/xdg_activation.rs @@ -243,7 +243,7 @@ impl State { { for mapped in current_workspace .mapped() - .filter(|m| m.maximized_state.lock().unwrap().is_some()) + .filter(|m| m.maximized_state.lock().unwrap().is_some() && *m != &element) .cloned() .collect::>() .into_iter() From 0fbd4574ef4caf74769a617d205fd1fc909ac9b1 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Tue, 22 Sep 2026 14:41:11 -0400 Subject: [PATCH 79/80] fix: skip loading shortcuts for kiosk mode --- src/config/mod.rs | 70 ++++++++++++++++++++++++++--------------------- src/state.rs | 2 +- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 4d2fad9b..f8a51585 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -172,7 +172,7 @@ pub enum ColorFilter { } impl Config { - pub fn load(loop_handle: &LoopHandle<'_, State>) -> Config { + pub fn load(loop_handle: &LoopHandle<'_, State>, kiosk_mode: bool) -> Config { let config = cosmic_config::Config::new("com.system76.CosmicComp", 1).unwrap(); let source = cosmic_config::calloop::ConfigWatchSource::new(&config).unwrap(); loop_handle @@ -248,40 +248,48 @@ impl Config { // Source key bindings from com.system76.CosmicSettings.Shortcuts let settings_context = shortcuts::context().expect("Failed to load shortcuts config"); - let system_actions = shortcuts::system_actions(&settings_context); - let shortcuts = shortcuts::shortcuts(&settings_context); + let mut system_actions = Default::default(); + let mut shortcuts = Default::default(); + // Kiosk mode disables shortcuts + if !kiosk_mode { + system_actions = shortcuts::system_actions(&settings_context); + shortcuts = shortcuts::shortcuts(&settings_context); - // Listen for updates to the keybindings config. - match cosmic_config::calloop::ConfigWatchSource::new(&settings_context) { - Ok(source) => { - if let Err(err) = loop_handle.insert_source(source, |(config, keys), (), state| { - for key in keys { - match key.as_str() { - // Reload the keyboard shortcuts config. - "custom" | "defaults" => { - state.common.config.shortcuts = shortcuts::shortcuts(&config); + // Listen for updates to the keybindings config. + match cosmic_config::calloop::ConfigWatchSource::new(&settings_context) { + Ok(source) => { + if let Err(err) = + loop_handle.insert_source(source, |(config, keys), (), state| { + for key in keys { + match key.as_str() { + // Reload the keyboard shortcuts config. + "custom" | "defaults" => { + state.common.config.shortcuts = + shortcuts::shortcuts(&config); + } + + "system_actions" => { + state.common.config.system_actions = + shortcuts::system_actions(&config); + } + + _ => (), + } } - - "system_actions" => { - state.common.config.system_actions = - shortcuts::system_actions(&config); - } - - _ => (), - } + }) + { + warn!( + ?err, + "Failed to watch com.system76.CosmicSettings.Shortcuts config" + ); } - }) { - warn!( - ?err, - "Failed to watch com.system76.CosmicSettings.Shortcuts config" - ); } - } - Err(err) => warn!( - ?err, - "failed to create config watch source for com.system76.CosmicSettings.Shortcuts" - ), - }; + Err(err) => warn!( + ?err, + "failed to create config watch source for com.system76.CosmicSettings.Shortcuts" + ), + }; + } let window_rules_context = window_rules::context().expect("Failed to load window rules config"); diff --git a/src/state.rs b/src/state.rs index d605c736..86f5e23e 100644 --- a/src/state.rs +++ b/src/state.rs @@ -667,7 +667,7 @@ impl State { .unwrap(); let clock = Clock::new(); - let config = Config::load(&handle); + let config = Config::load(&handle, kiosk_command.is_some()); let compositor_state = CompositorState::new::(dh); let corner_radius_state = CornerRadiusState::new::(dh); let data_device_state = DataDeviceState::new::(dh); From 30d8868cca9426e9f08e74e45a6a754908f102ec Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Thu, 24 Sep 2026 21:03:54 +0200 Subject: [PATCH 80/80] chore: fige Cargo.lock (epoch-1.9.0, [patch] locaux) Co-Authored-By: Claude Opus 5.5 (1M context) --- Cargo.lock | 1201 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 1001 insertions(+), 200 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 93ad9171..c27ed2cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,61 @@ 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?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +dependencies = [ + "uuid", +] + +[[package]] +name = "accesskit_consumer" +version = "0.32.0" +source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +dependencies = [ + "accesskit", + "hashbrown 0.16.1", +] + +[[package]] +name = "accesskit_macos" +version = "0.23.0" +source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +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_windows" +version = "0.30.0" +source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +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?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +dependencies = [ + "accesskit", + "accesskit_macos", + "accesskit_windows", + "raw-window-handle", + "winit 0.31.0-beta.2", +] + [[package]] name = "addr2line" version = "0.25.1" @@ -61,6 +116,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" @@ -166,6 +227,25 @@ dependencies = [ "libloading", ] +[[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.5", + "serde", + "serde_repr", + "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus", +] + [[package]] name = "async-broadcast" version = "0.7.2" @@ -343,7 +423,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -412,13 +492,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]] @@ -463,6 +552,13 @@ dependencies = [ "num-traits", ] +[[package]] +name = "build_helpers" +version = "0.14.0" +dependencies = [ + "cfg_aliases", +] + [[package]] name = "bumpalo" version = "3.20.2" @@ -594,7 +690,7 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -615,7 +711,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", @@ -625,22 +720,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" @@ -650,7 +735,7 @@ dependencies = [ "bitflags 1.3.2", "block", "cocoa-foundation", - "core-foundation", + "core-foundation 0.9.4", "core-graphics", "foreign-types", "libc", @@ -665,8 +750,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", ] @@ -677,7 +762,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]] @@ -721,6 +808,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" @@ -734,8 +831,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", ] @@ -747,7 +844,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.13.2", + "core-foundation 0.10.1", "libc", ] @@ -760,6 +868,18 @@ dependencies = [ "libm", ] +[[package]] +name = "cosmic-client-toolkit" +version = "0.2.0" +dependencies = [ + "bitflags 2.13.2", + "cosmic-protocols", + "libc", + "smithay-client-toolkit", + "wayland-client", + "wayland-protocols", +] + [[package]] name = "cosmic-comp" version = "1.0.0" @@ -787,7 +907,7 @@ dependencies = [ "indexmap 2.13.0", "jiff", "keyframe", - "libcosmic", + "libcosmic-yoda", "libdisplay-info", "libsystemd", "log-panics", @@ -809,7 +929,7 @@ dependencies = [ "smithay", "smithay-egui", "thiserror 2.0.18", - "tiny-skia", + "tiny-skia 0.11.4", "tracing", "tracing-journald", "tracing-subscriber", @@ -839,7 +959,6 @@ dependencies = [ [[package]] name = "cosmic-config" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "atomicwrites", "calloop", @@ -860,7 +979,6 @@ dependencies = [ [[package]] name = "cosmic-config-derive" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "quote", "syn", @@ -869,12 +987,11 @@ dependencies = [ [[package]] name = "cosmic-freedesktop-icons" version = "0.4.0" -source = "git+https://github.com/pop-os/freedesktop-icons#ab4c57b8e416c6af9297cb04d101889896fd9a92" dependencies = [ "bstr", "btoi", "memchr", - "memmap2", + "memmap2 0.9.10", "thiserror 2.0.18", "tracing", "xdg", @@ -883,10 +1000,10 @@ dependencies = [ [[package]] name = "cosmic-protocols" version = "0.2.0" -source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#c0cff4db14c37ed954983158e4055aa94c7741d9" dependencies = [ "bitflags 2.13.2", "wayland-backend", + "wayland-client", "wayland-protocols", "wayland-protocols-wlr", "wayland-scanner", @@ -895,8 +1012,7 @@ dependencies = [ [[package]] name = "cosmic-randr-shell" -version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-randr/#6e8e795970fa06d434af22775e415b517f7552d3" +version = "1.3.0" dependencies = [ "kdl", "slotmap", @@ -905,8 +1021,7 @@ dependencies = [ [[package]] name = "cosmic-settings-config" -version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-settings-daemon#e37160f14d1e7ee428f973cd2848b4e95f83dfe1" +version = "1.9.0" dependencies = [ "cosmic-config", "ron 0.11.0", @@ -918,16 +1033,14 @@ dependencies = [ [[package]] name = "cosmic-settings-daemon" -version = "0.1.0" -source = "git+https://github.com/pop-os/dbus-settings-bindings#eed01dd3609e90e3c8cd043656734c500956c793" +version = "0.1.1-yoda.1" dependencies = [ "zbus", ] [[package]] name = "cosmic-settings-daemon-config" -version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-settings-daemon#e37160f14d1e7ee428f973cd2848b4e95f83dfe1" +version = "1.9.0" dependencies = [ "cosmic-config", "cosmic-theme", @@ -937,8 +1050,6 @@ dependencies = [ [[package]] name = "cosmic-text" version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be17b688510d934ce13f48a2beba700e11583e281e0fda99c22bb256a14eda73" dependencies = [ "bitflags 2.13.2", "fontdb", @@ -956,12 +1067,12 @@ 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#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "almost", "configparser", @@ -1018,7 +1129,6 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cryoglyph" version = "0.1.0" -source = "git+https://github.com/iced-rs/cryoglyph.git?rev=e429a025df36ab8145708acb309080ae3deec17a#e429a025df36ab8145708acb309080ae3deec17a" dependencies = [ "cosmic-text", "etagere", @@ -1170,7 +1280,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1180,7 +1290,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ "bitflags 2.13.2", - "objc2", + "objc2 0.6.4", ] [[package]] @@ -1206,7 +1316,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.13.2", "mime 0.1.0", @@ -1233,21 +1342,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.13.2", - "bytemuck", - "drm-ffi 0.7.1", - "drm-fourcc", - "rustix 0.38.44", -] +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] name = "drm" @@ -1257,29 +1357,19 @@ checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f" dependencies = [ "bitflags 2.13.2", "bytemuck", - "drm-ffi 0.9.1", + "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.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51a91c9b32ac4e8105dec255e849e0d66e27d7c34d184364fb93e469db08f690" dependencies = [ - "drm-sys 0.8.1", + "drm-sys", "rustix 1.1.4", ] @@ -1289,16 +1379,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.1" @@ -1484,7 +1564,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -1698,7 +1778,7 @@ checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" dependencies = [ "fontconfig-parser", "log", - "memmap2", + "memmap2 0.9.10", "slotmap", "tinyvec", "ttf-parser", @@ -1857,7 +1937,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce852e998d3ca5e4a97014fb31c940dc5ef344ec7d364984525fd11e8a547e6a" dependencies = [ "bitflags 2.13.2", - "drm 0.14.1", + "drm", "drm-fourcc", "gbm-sys", "libc", @@ -1883,8 +1963,8 @@ dependencies = [ "libc", "log", "rustversion", - "windows-link", - "windows-result", + "windows-link 0.2.1", + "windows-result 0.4.1", ] [[package]] @@ -1904,7 +1984,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]] @@ -1995,6 +2075,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.13.2", + "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.13.2", +] + [[package]] name = "grid" version = "1.0.0" @@ -2057,6 +2180,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", ] @@ -2204,7 +2329,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.62.2", ] [[package]] @@ -2219,9 +2344,10 @@ dependencies = [ [[package]] name = "iced" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ + "build_helpers", "dnd", + "iced_accessibility", "iced_core", "iced_debug", "iced_futures", @@ -2229,19 +2355,29 @@ 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#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "bitflags 2.13.2", + "build_helpers", "bytes", + "cosmic-client-toolkit", "dnd", "glam 0.25.0", "lilt", @@ -2262,7 +2398,6 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "iced_core", "iced_futures", @@ -2272,7 +2407,6 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "futures", "iced_core", @@ -2285,7 +2419,6 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "bitflags 2.13.2", "bytemuck", @@ -2306,7 +2439,6 @@ dependencies = [ [[package]] name = "iced_program" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "iced_graphics", "iced_runtime", @@ -2315,7 +2447,6 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -2327,9 +2458,10 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ + "build_helpers", "bytes", + "cosmic-client-toolkit", "dnd", "iced_core", "iced_futures", @@ -2341,7 +2473,6 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "bytemuck", "cosmic-text", @@ -2352,17 +2483,17 @@ dependencies = [ "resvg 0.45.1", "rustc-hash 2.1.1", "softbuffer", - "tiny-skia", + "tiny-skia 0.11.4", ] [[package]] name = "iced_wgpu" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ - "as-raw-xcb-connection", "bitflags 2.13.2", + "build_helpers", "bytemuck", + "cosmic-client-toolkit", "cryoglyph", "futures", "glam 0.25.0", @@ -2376,20 +2507,19 @@ 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#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ + "build_helpers", + "cosmic-client-toolkit", "dnd", "iced_renderer", "iced_runtime", @@ -2402,6 +2532,38 @@ dependencies = [ "window_clipboard", ] +[[package]] +name = "iced_winit" +version = "0.14.0" +dependencies = [ + "build_helpers", + "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" @@ -2671,7 +2833,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2782,6 +2944,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" @@ -2794,7 +2967,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1886916523694cd6ea3d175f03a1e5010699a2a4cc13696d83d7bea1d80638" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2866,12 +3039,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] -name = "libcosmic" -version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" +name = "libcosmic-yoda" +version = "0.1.0-yoda.2" dependencies = [ "apply", + "ashpd", "auto_enums", + "build_helpers", + "cosmic-client-toolkit", "cosmic-config", "cosmic-freedesktop-icons", "cosmic-settings-daemon", @@ -2889,12 +3064,15 @@ dependencies = [ "iced_renderer", "iced_runtime", "iced_tiny_skia", + "iced_wgpu", "iced_widget", + "iced_winit", "image", "jiff", "log", "palette", "phf 0.13.1", + "roxmltree 0.20.0", "rust-embed", "serde", "slotmap", @@ -2947,7 +3125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -2958,13 +3136,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.12" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +checksum = "61ff90caf6077a803a240f62fdbe88645a890bbca49ef8174c3cb0404362171d" dependencies = [ "bitflags 2.13.2", "libc", - "redox_syscall 0.7.2", + "plain", + "redox_syscall 0.9.4", ] [[package]] @@ -3036,12 +3215,6 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" -[[package]] -name = "linux-raw-sys" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" - [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -3196,6 +3369,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" @@ -3214,6 +3396,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.13.2", + "block", + "core-graphics-types 0.2.0", + "foreign-types", + "log", + "objc", + "paste", +] + [[package]] name = "miette" version = "7.6.0" @@ -3221,13 +3418,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", ] @@ -3315,6 +3511,7 @@ dependencies = [ "num-traits", "once_cell", "rustc-hash 1.1.0", + "spirv", "thiserror 2.0.18", "unicode-ident", ] @@ -3410,7 +3607,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3535,6 +3732,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" @@ -3544,6 +3757,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.13.2", + "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" @@ -3551,9 +3780,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ "bitflags 2.13.2", - "objc2", + "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.13.2", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -3563,9 +3804,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ "bitflags 2.13.2", - "block2", + "block2 0.6.2", "dispatch2", - "objc2", + "objc2 0.6.4", ] [[package]] @@ -3579,6 +3820,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" @@ -3596,6 +3849,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.13.2", + "block2 0.5.1", + "libc", + "objc2 0.5.2", +] + [[package]] name = "objc2-foundation" version = "0.3.2" @@ -3603,11 +3868,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.13.2", - "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.13.2", + "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.13.2", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + [[package]] name = "objc2-ui-kit" version = "0.3.2" @@ -3615,9 +3905,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ "bitflags 2.13.2", - "objc2", + "objc2 0.6.4", "objc2-core-foundation", - "objc2-foundation", + "objc2-foundation 0.3.2", ] [[package]] @@ -3763,7 +4053,7 @@ dependencies = [ "libc", "redox_syscall 0.5.18", "smallvec", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -3940,6 +4230,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" @@ -4019,6 +4315,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" @@ -4148,10 +4450,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", + "rand_chacha 0.3.1", "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + [[package]] name = "rand" version = "0.10.0" @@ -4173,6 +4485,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + [[package]] name = "rand_core" version = "0.6.4" @@ -4182,12 +4504,27 @@ dependencies = [ "getrandom 0.2.17", ] +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + [[package]] name = "rand_core" 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" @@ -4227,6 +4564,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.13.2", + "libredox", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -4238,9 +4585,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.7.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d94dd2f7cd932d4dc02cc8b2b50dfd38bd079a4e5d79198b99743d7fcf9a4b4" +checksum = "737970939a87c6fa31e7acad13307bccbb017a073b695b6089a2c484f929e20e" dependencies = [ "bitflags 2.13.2", ] @@ -4333,7 +4680,7 @@ dependencies = [ "pico-args", "rgb", "svgtypes 0.13.0", - "tiny-skia", + "tiny-skia 0.11.4", "usvg 0.37.0", ] @@ -4349,7 +4696,7 @@ dependencies = [ "pico-args", "rgb", "svgtypes 0.15.3", - "tiny-skia", + "tiny-skia 0.11.4", "usvg 0.45.1", "zune-jpeg 0.4.21", ] @@ -4477,7 +4824,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4558,6 +4905,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sctk-adwaita" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2a4360b9abcdcee43809e6fc11d6d61ca5d25734026a8fc46c2883eea3f13f" +dependencies = [ + "ab_glyph", + "log", + "memmap2 0.9.10", + "smithay-client-toolkit", + "tiny-skia 0.12.0", +] + [[package]] name = "self_cell" version = "1.2.2" @@ -4782,10 +5142,10 @@ dependencies = [ "cc", "cursor-icon", "downcast-rs", - "drm 0.14.1", - "drm-ffi 0.9.1", + "drm", + "drm-ffi", "drm-fourcc", - "drm-sys 0.8.1", + "drm-sys", "encoding_rs", "errno", "gbm", @@ -4819,7 +5179,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", ] @@ -4831,12 +5191,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" dependencies = [ "bitflags 2.13.2", + "bytemuck", "calloop", "calloop-wayland-source", "cursor-icon", "libc", "log", - "memmap2", + "memmap2 0.9.10", + "pkg-config", "rustix 1.1.4", "thiserror 2.0.18", "wayland-backend", @@ -4848,13 +5210,13 @@ dependencies = [ "wayland-protocols-misc", "wayland-protocols-wlr", "wayland-scanner", + "xkbcommon 0.8.0", "xkeysym", ] [[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", @@ -4895,22 +5257,20 @@ checksum = "27207bb65232eda1f588cf46db2fee75c0808d557f6b3cf19a75f5d6d7c94df1" [[package]] name = "softbuffer" version = "0.4.1" -source = "git+https://github.com/pop-os/softbuffer?tag=cosmic-4.0#c2b2c19ddb38ff17495643699f97cb1f2064a1be" dependencies = [ "as-raw-xcb-connection", "bytemuck", "cfg_aliases", "cocoa", "core-graphics", - "drm 0.11.1", "fastrand", "foreign-types", "js-sys", + "libredox", "log", - "memmap2", + "memmap2 0.9.10", "objc", "raw-window-handle", - "redox_syscall 0.5.18", "rustix 0.38.44", "tiny-xlib", "wasm-bindgen", @@ -4922,6 +5282,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.13.2", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -5064,7 +5433,16 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "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]] @@ -5159,7 +5537,21 @@ dependencies = [ "cfg-if", "log", "png 0.17.16", - "tiny-skia-path", + "tiny-skia-path 0.11.4", +] + +[[package]] +name = "tiny-skia" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47ffee5eaaf5527f630fb0e356b90ebdec84d5d18d937c5e440350f88c5a91ea" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "tiny-skia-path 0.12.0", ] [[package]] @@ -5173,6 +5565,17 @@ dependencies = [ "strict-num", ] +[[package]] +name = "tiny-skia-path" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca365c3faccca67d06593c5980fa6c57687de727a03131735bb85f01fdeeb9" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + [[package]] name = "tiny-xlib" version = "0.2.4" @@ -5513,6 +5916,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" @@ -5529,6 +5938,7 @@ dependencies = [ "idna", "percent-encoding", "serde", + "serde_derive", ] [[package]] @@ -5565,7 +5975,7 @@ dependencies = [ "siphasher 1.0.2", "strict-num", "svgtypes 0.15.3", - "tiny-skia-path", + "tiny-skia-path 0.11.4", "unicode-bidi", "unicode-script", "unicode-vo", @@ -5599,7 +6009,7 @@ dependencies = [ "rctree", "strict-num", "svgtypes 0.13.0", - "tiny-skia-path", + "tiny-skia-path 0.11.4", ] [[package]] @@ -5978,12 +6388,18 @@ dependencies = [ "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", @@ -6014,11 +6430,31 @@ 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-apple" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +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" @@ -6034,18 +6470,46 @@ version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d6cb474beb218824dcc9e1ce679d973f719262789bfb27407da560cac20eeb" dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", "bitflags 2.13.2", + "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]] @@ -6083,7 +6547,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -6095,18 +6559,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" @@ -6115,9 +6633,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]] @@ -6142,19 +6682,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]] @@ -6163,7 +6747,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]] @@ -6217,7 +6801,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]] @@ -6272,7 +6856,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", @@ -6283,6 +6867,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" @@ -6463,6 +7065,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.13.2", + "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" @@ -6472,24 +7098,38 @@ dependencies = [ "bitflags 2.13.2", "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.13.2", + "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" @@ -6498,12 +7138,33 @@ checksum = "51d9c0d2cd93efec3a9f9ad819cfaf0834782403af7c0d248c784ec0c61761df" dependencies = [ "android-activity", "bitflags 2.13.2", - "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.13.2", + "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]] @@ -6513,20 +7174,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21310ca07851a49c348e0c2cc768e36b52ca65afda2c2354d78ed4b90074d8aa" dependencies = [ "bitflags 2.13.2", - "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]] @@ -6535,16 +7209,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.13.2", + "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" @@ -6553,13 +7240,28 @@ checksum = "e4f0ccd7abb43740e2c6124ac7cae7d865ecec74eec63783e8922577ac232583" dependencies = [ "bitflags 2.13.2", "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.13.2", + "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" @@ -6567,13 +7269,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51ea1fb262e7209f265f12bd0cc792c399b14355675e65531e9c8a87db287d46" dependencies = [ "bitflags 2.13.2", - "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.13.2", + "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]] @@ -6583,18 +7304,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "680a356e798837d8eb274d4556e83bceaf81698194e31aafc5cfb8a9f2fab643" dependencies = [ "bitflags 2.13.2", - "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.13.2", + "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]] @@ -6607,9 +7353,9 @@ dependencies = [ "bitflags 2.13.2", "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", @@ -6619,8 +7365,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.13.2", + "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]] @@ -6633,7 +7400,7 @@ dependencies = [ "bitflags 2.13.2", "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", @@ -6643,7 +7410,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.13.2", + "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]] @@ -6654,13 +7436,13 @@ checksum = "644ea78af0e858aa3b092e5d1c67c41995a98220c81813f1353b28bc8bb91eaa" dependencies = [ "bitflags 2.13.2", "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]] @@ -6673,15 +7455,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", @@ -6863,6 +7645,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" @@ -6870,7 +7663,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d66ca9352cbd4eecbbc40871d8a11b4ac8107cfc528a6e14d7c19c69d0e1ac9" dependencies = [ "libc", - "memmap2", + "memmap2 0.9.10", "xkeysym", ] @@ -6881,7 +7674,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a974f48060a14e95705c01f24ad9c3345022f4d97441b8a36beb7ed5c4a02d" dependencies = [ "libc", - "memmap2", + "memmap2 0.9.10", "xkeysym", ] @@ -6903,6 +7696,9 @@ name = "xkeysym" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" +dependencies = [ + "bytemuck", +] [[package]] name = "xml-rs" @@ -7138,6 +7934,7 @@ dependencies = [ "endi", "enumflags2", "serde", + "url", "winnow 1.0.3", "zvariant_derive", "zvariant_utils", @@ -7168,3 +7965,7 @@ dependencies = [ "syn", "winnow 1.0.3", ] + +[[patch.unused]] +name = "winit-x11" +version = "0.31.0-beta.2"