diff --git a/Cargo.lock b/Cargo.lock index 007567b5..c4db0004 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -937,7 +937,6 @@ dependencies = [ "profiling", "rand 0.10.0", "regex", - "resvg 0.45.1", "ron 0.12.0", "rust-embed", "rustix 1.1.4", @@ -7646,9 +7645,9 @@ checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" [[package]] name = "xcursor" -version = "0.3.11" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "163b33ed8786455e2fa5d72f554057ce3f3182425434f756cd39c99839d88e23" +checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" [[package]] name = "xdg" diff --git a/Cargo.toml b/Cargo.toml index 145f1248..86bb5159 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,6 @@ 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/cosmic-comp-config/src/lib.rs b/cosmic-comp-config/src/lib.rs index 853ba447..e5e73883 100644 --- a/cosmic-comp-config/src/lib.rs +++ b/cosmic-comp-config/src/lib.rs @@ -101,8 +101,6 @@ 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, } @@ -141,7 +139,6 @@ 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(), } } @@ -198,10 +195,6 @@ 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/backend/render/cursor.rs b/src/backend/render/cursor.rs index 8e14d1be..da01b270 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -8,9 +8,6 @@ use crate::{ utils::prelude::*, wayland::handlers::compositor::FRAME_TIME_FILTER, }; -use keyframe::{ease, functions::EaseInOutCubic}; -use resvg::{tiny_skia, usvg}; -use serde::Deserialize; use smithay::{ backend::{ allocator::Fourcc, @@ -41,7 +38,7 @@ use smithay::{ wayland::compositor::{get_role, with_states}, }; use std::{ - collections::{HashMap, VecDeque}, + collections::HashMap, io::Read, sync::Mutex, time::{Duration, Instant}, @@ -54,43 +51,14 @@ 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 { - kind: CursorKind, + icons: Vec, 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)) @@ -107,134 +75,44 @@ impl Cursor { }] }); - Cursor { - kind: CursorKind::Xcursor(icons), - size, - } + Cursor { icons, size } } pub fn get_image(&self, scale: u32, millis: u32) -> Image { let size = self.size * scale; - 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), - } + frame(millis, size, &self.icons) } } -/// 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. +fn nearest_images(size: u32, images: &[Image]) -> impl Iterator { + // 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() - .enumerate() - .filter(|(_, image)| image.width == width && image.height == height) - .map(|(i, _)| i) - .collect() + images.iter().filter(move |image| { + image.width == nearest_image.width && image.height == nearest_image.height + }) } -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(); +fn frame(mut millis: u32, size: u32, images: &[Image]) -> Image { + let total = nearest_images(size, images).fold(0, |acc, image| acc + image.delay); if total == 0 { - return indices[0]; + millis = 0; + } else { + millis %= total; } - millis %= total; - for &i in &indices { - if millis <= images[i].delay { - return i; + for img in nearest_images(size, images) { + if millis <= img.delay { + return img.clone(); } - millis -= images[i].delay; + millis -= img.delay; } - *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 + unreachable!() } #[derive(thiserror::Error, Debug)] @@ -296,79 +174,6 @@ 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, @@ -460,62 +265,11 @@ pub struct CursorStateInner { cursors: HashMap, current_image: Option, - image_cache: Vec, + image_cache: Vec<(Image, MemoryRenderBuffer)>, 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, - 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. -#[derive(Clone, Copy)] -struct PathSample { - position: Point, - 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. -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; -/// 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); - -/// 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 { @@ -528,141 +282,14 @@ 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(cursor_theme, shape, cursor_size)) + .or_insert_with(|| Cursor::load(&self.cursor_theme, shape, self.cursor_size)) } pub fn size(&self) -> u32 { 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. - 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) { @@ -697,34 +324,10 @@ 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, - rest_started: None, } } } -/// 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, @@ -760,53 +363,32 @@ pub fn draw_cursor( return; } - 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); + 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); - // 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. - let index = match state.image_cache.iter().position(|frame| frame.key == key) { - Some(index) => index, + 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 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, + &frame.pixels_rgba, Fourcc::Argb8888, - (image.width as i32, image.height as i32), + (frame.width as i32, frame.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 + pointer_images.push((frame.clone(), buffer)); + pointer_images.last().map(|(_, i)| i).unwrap() } }; - let (frame, pointer_image) = { - 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); - let hotspot = Point::::from((frame.xhot as i32, frame.yhot as i32)) .to_logical( actual_scale as i32, @@ -820,7 +402,7 @@ pub fn draw_cursor( MemoryRenderBufferRenderElement::from_buffer( renderer, location.to_physical(scale), - &pointer_image, + pointer_image, None, None, None, diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index cd2fb5c2..e1259dc1 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -41,9 +41,7 @@ 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 b348640f..e9cf651f 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -510,35 +510,22 @@ 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 * cursor_magnification as f64, + zoom_scale, now, blur_strength, mode != CursorMode::NotDefault, &mut |elem, hotspot| { push(CosmicElement::Cursor(RescaleRenderElement::from_element( - RescaleRenderElement::from_element( - RelocateRenderElement::from_element( - elem, - Point::from((-hotspot.x, -hotspot.y)), - Relocate::Relative, - ), - cursor_center, - cursor_magnification as f64, + RelocateRenderElement::from_element( + elem, + Point::from((-hotspot.x, -hotspot.y)), + Relocate::Relative, ), focal_point .as_logical() diff --git a/src/config/mod.rs b/src/config/mod.rs index 93b53374..4a2500fd 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -969,10 +969,6 @@ 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/actions.rs b/src/input/actions.rs index 8a5ef84c..a4932597 100644 --- a/src/input/actions.rs +++ b/src/input/actions.rs @@ -29,8 +29,6 @@ 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, @@ -1118,14 +1116,7 @@ impl State { } if zoom_seat == *seat { - let factor = 1.0 + change.abs(); - let new_level = if change < 0. { - current_level / factor - } else { - current_level * factor - } - .clamp(1.0, MAX_ZOOM); - let new_level = if new_level < 1.01 { 1.0 } else { new_level }; + let new_level = (current_level + change).max(1.0); shell.trigger_zoom( seat, Some(&output), diff --git a/src/input/mod.rs b/src/input/mod.rs index 0e307079..67780d53 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -337,21 +337,6 @@ 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 009c98dc..d9f95976 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1614,29 +1614,6 @@ 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) { @@ -2309,11 +2286,6 @@ 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 { diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index 25a16e9c..8363510a 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::{ZoomConfig, ZoomMovement}; +use cosmic_comp_config::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 = ZoomConfig::ZOOM_INCREMENT_PRESETS.to_vec(); + let mut increments = vec![25, 50, 100, 150, 200]; if !increments.contains(&increment) { increments.push(increment); } @@ -829,7 +829,7 @@ impl Program for ZoomProgram { if let Some(pos) = self.increments.iter().position(|val| *val == increment) { self.increment_idx = pos; } else { - let mut increments = ZoomConfig::ZOOM_INCREMENT_PRESETS.to_vec(); + let mut increments = vec![25, 50, 100, 150, 200]; if !increments.contains(&increment) { increments.push(increment); } diff --git a/src/wayland/handlers/session_lock.rs b/src/wayland/handlers/session_lock.rs index c4e93ec5..39e1e375 100644 --- a/src/wayland/handlers/session_lock.rs +++ b/src/wayland/handlers/session_lock.rs @@ -46,11 +46,6 @@ 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); }