From afdb65677857946b403043f04ca083810639e4e5 Mon Sep 17 00:00:00 2001 From: Christian Meissl Date: Fri, 13 Sep 2024 19:22:57 +0200 Subject: [PATCH] dnd: correct handling of cursor buffer offset --- src/backend/render/mod.rs | 13 +++- src/shell/mod.rs | 42 ++++++++++- src/wayland/handlers/data_device.rs | 89 +++++++++++++++++++---- src/wayland/handlers/screencopy/render.rs | 13 +++- 4 files changed, 130 insertions(+), 27 deletions(-) diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index c49929e8..5722f300 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -451,11 +451,16 @@ where } if !exclude_dnd_icon { - if let Some(wl_surface) = get_dnd_icon(&seat) { + if let Some(dnd_icon) = get_dnd_icon(&seat) { elements.extend( - cursor::draw_dnd_icon(renderer, &wl_surface, location.to_i32_round(), scale) - .into_iter() - .map(CosmicElement::Dnd), + cursor::draw_dnd_icon( + renderer, + &dnd_icon.surface, + (location + dnd_icon.offset.to_f64()).to_i32_round(), + scale, + ) + .into_iter() + .map(CosmicElement::Dnd), ); } } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 5a4824ea..c32f5cc5 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -5,12 +5,12 @@ use indexmap::IndexMap; use layout::TilingExceptions; use std::{ collections::HashMap, - sync::atomic::Ordering, + sync::{atomic::Ordering, Mutex}, time::{Duration, Instant}, }; use wayland_backend::server::ClientId; -use crate::wayland::protocols::workspace::WorkspaceCapabilities; +use crate::wayland::{handlers::data_device, protocols::workspace::WorkspaceCapabilities}; use cosmic_comp_config::{ workspace::{WorkspaceLayout, WorkspaceMode}, TileBehavior, @@ -33,7 +33,9 @@ use smithay::{ LayerSurface, PopupKind, WindowSurface, WindowSurfaceType, }, input::{ - pointer::{Focus, GrabStartData as PointerGrabStartData}, + pointer::{ + CursorImageStatus, CursorImageSurfaceData, Focus, GrabStartData as PointerGrabStartData, + }, Seat, }, output::Output, @@ -43,7 +45,8 @@ use smithay::{ }, utils::{IsAlive, Logical, Point, Rectangle, Serial, Size}, wayland::{ - compositor::with_states, + compositor::{with_states, SurfaceAttributes}, + foreign_toplevel_list::ForeignToplevelListState, seat::WaylandFocus, session_lock::LockSurface, shell::wlr_layer::{KeyboardInteractivity, Layer, LayerSurfaceCachedState}, @@ -1216,6 +1219,37 @@ impl Common { } } } + + data_device::on_commit(surface, seat); + } + + let is_cursor_image = shell.seats.iter().any(|seat| { + seat.user_data() + .get::>() + .map(|guard| { + matches!(*guard.lock().unwrap(), CursorImageStatus::Surface(ref cursor_surface) if cursor_surface == surface) + }) + .unwrap_or(false) + }); + + if is_cursor_image { + with_states(surface, |states| { + let cursor_image_attributes = states.data_map.get::(); + + if let Some(mut cursor_image_attributes) = + cursor_image_attributes.map(|attrs| attrs.lock().unwrap()) + { + let buffer_delta = states + .cached_state + .get::() + .current() + .buffer_delta + .take(); + if let Some(buffer_delta) = buffer_delta { + cursor_image_attributes.hotspot -= buffer_delta; + } + } + }); } if let Some(mapped) = shell.element_for_surface(surface) { diff --git a/src/wayland/handlers/data_device.rs b/src/wayland/handlers/data_device.rs index 61b4564d..913aef92 100644 --- a/src/wayland/handlers/data_device.rs +++ b/src/wayland/handlers/data_device.rs @@ -3,25 +3,61 @@ use crate::state::State; use smithay::{ delegate_data_device, - input::Seat, + input::{ + pointer::{CursorImageStatus, CursorImageSurfaceData}, + Seat, + }, reexports::wayland_server::protocol::{wl_data_source::WlDataSource, wl_surface::WlSurface}, - utils::IsAlive, - wayland::selection::data_device::{ - ClientDndGrabHandler, DataDeviceHandler, DataDeviceState, ServerDndGrabHandler, + utils::{IsAlive, Logical, Point}, + wayland::{ + compositor::{self, SurfaceAttributes}, + selection::data_device::{ + ClientDndGrabHandler, DataDeviceHandler, DataDeviceState, ServerDndGrabHandler, + }, }, }; use std::sync::Mutex; +#[derive(Debug, Clone)] pub struct DnDIcon { - surface: Mutex>, + pub surface: WlSurface, + pub offset: Point, } -pub fn get_dnd_icon(seat: &Seat) -> Option { +pub fn get_dnd_icon(seat: &Seat) -> Option { let userdata = seat.user_data(); userdata - .get::() - .and_then(|x| x.surface.lock().unwrap().clone()) - .filter(IsAlive::alive) + .get::>>() + .and_then(|x| x.lock().unwrap().clone()) + .filter(|icon| icon.surface.alive()) +} + +pub fn on_commit(surface: &WlSurface, seat: &Seat) { + let userdata = seat.user_data(); + + let Some(mut guard) = userdata + .get::>>() + .map(|guard| guard.lock().unwrap()) + else { + return; + }; + + let Some(icon) = guard.as_mut() else { + return; + }; + + if &icon.surface == surface { + compositor::with_states(surface, |states| { + let buffer_delta = states + .cached_state + .get::() + .current() + .buffer_delta + .take() + .unwrap_or_default(); + icon.offset += buffer_delta; + }); + } } impl ClientDndGrabHandler for State { @@ -32,16 +68,39 @@ impl ClientDndGrabHandler for State { seat: Seat, ) { let user_data = seat.user_data(); - user_data.insert_if_missing_threadsafe(|| DnDIcon { - surface: Mutex::new(None), - }); - *user_data.get::().unwrap().surface.lock().unwrap() = icon; + user_data.insert_if_missing_threadsafe::>, _>(|| Default::default()); + + let offset = seat + .user_data() + .get::>() + .map(|guard| { + if let CursorImageStatus::Surface(ref surface) = *guard.lock().unwrap() { + compositor::with_states(surface, |states| { + let hotspot = states + .data_map + .get::() + .unwrap() + .lock() + .unwrap() + .hotspot; + Point::from((-hotspot.x, -hotspot.y)) + }) + } else { + (0, 0).into() + } + }) + .unwrap_or_default(); + + *user_data + .get::>>() + .unwrap() + .lock() + .unwrap() = icon.map(|surface| DnDIcon { surface, offset }) } fn dropped(&mut self, seat: Seat) { seat.user_data() - .get::() + .get::>>() .unwrap() - .surface .lock() .unwrap() .take(); diff --git a/src/wayland/handlers/screencopy/render.rs b/src/wayland/handlers/screencopy/render.rs index b0bd12ec..54f3070e 100644 --- a/src/wayland/handlers/screencopy/render.rs +++ b/src/wayland/handlers/screencopy/render.rs @@ -540,11 +540,16 @@ pub fn render_window_to_buffer( ); } - if let Some(wl_surface) = get_dnd_icon(&seat) { + if let Some(dnd_icon) = get_dnd_icon(&seat) { elements.extend( - cursor::draw_dnd_icon(renderer, &wl_surface, location.to_i32_round(), 1.0) - .into_iter() - .map(WindowCaptureElement::from), + cursor::draw_dnd_icon( + renderer, + &dnd_icon.surface, + (location + dnd_icon.offset.to_f64()).to_i32_round(), + 1.0, + ) + .into_iter() + .map(WindowCaptureElement::from), ); } }