diff --git a/Cargo.lock b/Cargo.lock index 91f8f8b4..7fd39620 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4952,7 +4952,7 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/smithay/smithay.git?rev=d40ada5#d40ada55eceac04fb1c752f0f17b0c32cf24fbb1" +source = "git+https://github.com/smithay/smithay.git?rev=593b2d5#593b2d5fb32a24652d8cde12b53d6871d1bd2275" dependencies = [ "aliasable", "appendlist", diff --git a/Cargo.toml b/Cargo.toml index 041ecd43..b3cef7bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -146,4 +146,4 @@ cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch cosmic-client-toolkit = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } [patch.crates-io] -smithay = { git = "https://github.com/smithay/smithay.git", rev = "d40ada5" } +smithay = { git = "https://github.com/smithay/smithay.git", rev = "593b2d5" } diff --git a/src/debug.rs b/src/debug.rs index f4218ef6..f167213c 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -287,6 +287,12 @@ fn format_pointer_focus(focus: Option) -> String { } _ => format!("Surface {}", surface.id().protocol_id()), }, + Some(X11Surface { surface, toplevel }) => match toplevel { + Some(window) => { + format!("Window {} ({})", surface.window_id(), window.title()) + } + _ => format!("X11Surface {}", surface.window_id()), + }, Some(StackUI(stack)) => format!( "Stack SSD {} ({})", match stack.active().0.underlying_surface() { diff --git a/src/input/mod.rs b/src/input/mod.rs index efd94e7f..7672be84 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -502,18 +502,34 @@ impl State { ptr.frame(self); return; } - if let PointerFocusTarget::WlSurface { surface, .. } = surface { - if under_from_surface_tree( - surface, - position.as_logical() - surface_loc.to_f64(), - (0, 0), - WindowSurfaceType::ALL, - ) - .is_none() - { - ptr.frame(self); - return; + match surface { + PointerFocusTarget::WlSurface { surface, .. } => { + if under_from_surface_tree( + surface, + position.as_logical() - surface_loc.to_f64(), + (0, 0), + WindowSurfaceType::ALL, + ) + .is_none() + { + ptr.frame(self); + return; + } } + PointerFocusTarget::X11Surface { surface, .. } => { + if surface + .surface_under( + position.as_logical() - surface_loc.to_f64(), + (0, 0), + WindowSurfaceType::ALL, + ) + .is_none() + { + ptr.frame(self); + return; + } + } + _ => {} } if let Some(region) = confine_region { if !region @@ -2243,21 +2259,18 @@ impl State { } } Stage::OverrideRedirect { surface, location } => { - if let Some(surface) = surface.wl_surface() { - if let Some((surface, surface_loc)) = under_from_surface_tree( - &surface, - global_pos.as_logical(), - location.as_logical(), - WindowSurfaceType::ALL, - ) { - return ControlFlow::Break(Ok(Some(( - PointerFocusTarget::WlSurface { - surface, - toplevel: None, - }, - surface_loc.as_global().to_f64(), - )))); - } + if let Some((_, surface_loc)) = surface.surface_under( + global_pos.as_logical(), + location.as_logical(), + WindowSurfaceType::ALL, + ) { + return ControlFlow::Break(Ok(Some(( + PointerFocusTarget::X11Surface { + surface: surface.clone(), + toplevel: None, + }, + surface_loc.as_global().to_f64(), + )))); } } Stage::StickyPopups(floating_layer) => { diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index e169446f..f9a82497 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -523,18 +523,14 @@ impl CosmicStack { let active_window = &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]; stack_ui.or_else(|| { - active_window - .0 - .surface_under(relative_pos, surface_type) - .map(|(surface, surface_offset)| { + active_window.focus_under(relative_pos, surface_type).map( + |(target, surface_offset)| { ( - PointerFocusTarget::WlSurface { - surface, - toplevel: Some(active_window.clone().into()), - }, + target, surface_offset.to_f64() + Point::from((0., TAB_HEIGHT as f64)), ) - }) + }, + ) }) }) } diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index fb6c9ae2..93f9ab0b 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -1,4 +1,6 @@ -use crate::wayland::protocols::corner_radius::CacheableCorners; +use crate::{ + shell::focus::target::PointerFocusTarget, wayland::protocols::corner_radius::CacheableCorners, +}; use std::{ borrow::Cow, sync::{ @@ -618,6 +620,38 @@ impl CosmicSurface { } } + pub fn focus_under( + &self, + relative_pos: Point, + surface_type: WindowSurfaceType, + ) -> Option<(PointerFocusTarget, Point)> { + if let Some(xsurface) = self.x11_surface() { + xsurface + .surface_under(relative_pos, Point::default(), surface_type) + .map(|(_surface, surface_offset)| { + ( + PointerFocusTarget::X11Surface { + surface: xsurface.clone(), + toplevel: Some(self.clone()), + }, + surface_offset.to_f64(), + ) + }) + } else { + self.0 + .surface_under(relative_pos, surface_type) + .map(|(surface, surface_offset)| { + ( + PointerFocusTarget::WlSurface { + surface, + toplevel: Some(self.clone().into()), + }, + surface_offset.to_f64(), + ) + }) + } + } + pub fn on_commit(&self) { self.0.on_commit(); } diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 0dc480b2..c2edbbad 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -281,17 +281,9 @@ impl CosmicWindow { } window_ui.or_else(|| { - p.window.0.surface_under(relative_pos, surface_type).map( - |(surface, surface_offset)| { - ( - PointerFocusTarget::WlSurface { - surface, - toplevel: Some(p.window.clone().into()), - }, - (offset + surface_offset.to_f64()), - ) - }, - ) + p.window + .focus_under(relative_pos, surface_type) + .map(|(target, surface_offset)| (target, offset + surface_offset)) }) }) } diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index c3c78ffb..b5d8b8dd 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -1,4 +1,8 @@ -use std::{borrow::Cow, sync::Weak, time::Duration}; +use std::{ + borrow::Cow, + sync::{Arc, Weak}, + time::Duration, +}; use crate::{ shell::{ @@ -16,6 +20,7 @@ use smithay::{ desktop::{LayerSurface, PopupKind, WindowSurface, WindowSurfaceType, space::SpaceElement}, input::{ Seat, + dnd::{DndFocus, OfferData, Source}, keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, pointer::{ AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, @@ -29,11 +34,14 @@ use smithay::{ }, }, reexports::wayland_server::{ - Client, Resource, backend::ObjectId, protocol::wl_surface::WlSurface, + Client, DisplayHandle, Resource, backend::ObjectId, protocol::wl_surface::WlSurface, }, utils::{IsAlive, Logical, Point, Serial, Transform}, - wayland::{seat::WaylandFocus, session_lock::LockSurface}, - xwayland::{X11Surface, xwm::XwmId}, + wayland::{seat::WaylandFocus, selection::data_device::WlOfferData, session_lock::LockSurface}, + xwayland::{ + X11Surface, + xwm::{XwmId, XwmOfferData}, + }, }; #[derive(Debug, Clone, PartialEq)] @@ -42,6 +50,10 @@ pub enum PointerFocusTarget { surface: WlSurface, toplevel: Option, }, + X11Surface { + surface: X11Surface, + toplevel: Option, + }, StackUI(CosmicStack), WindowUI(CosmicWindow), ResizeFork(ResizeForkTarget), @@ -82,16 +94,32 @@ impl From for PointerFocusTarget { match target { KeyboardFocusTarget::Element(elem) => { let window = elem.active_window(); - let surface = window.wl_surface().unwrap(); - PointerFocusTarget::WlSurface { - surface: surface.into_owned(), - toplevel: Some(window.into()), + + if let Some(xsurface) = window.x11_surface() { + PointerFocusTarget::X11Surface { + surface: xsurface.clone(), + toplevel: Some(window), + } + } else { + PointerFocusTarget::WlSurface { + surface: window.wl_surface().unwrap().into_owned(), + toplevel: Some(window.into()), + } + } + } + KeyboardFocusTarget::Fullscreen(elem) => { + if let Some(xsurface) = elem.x11_surface() { + PointerFocusTarget::X11Surface { + surface: xsurface.clone(), + toplevel: Some(elem), + } + } else { + PointerFocusTarget::WlSurface { + surface: elem.wl_surface().unwrap().into_owned(), + toplevel: Some(elem.into()), + } } } - KeyboardFocusTarget::Fullscreen(elem) => PointerFocusTarget::WlSurface { - surface: elem.wl_surface().unwrap().into_owned(), - toplevel: Some(elem.into()), - }, KeyboardFocusTarget::LayerSurface(layer) => PointerFocusTarget::WlSurface { surface: layer.wl_surface().clone(), toplevel: None, @@ -113,6 +141,7 @@ impl PointerFocusTarget { fn inner_pointer_target(&self) -> &dyn PointerTarget { match self { PointerFocusTarget::WlSurface { surface, .. } => surface, + PointerFocusTarget::X11Surface { surface, .. } => surface, PointerFocusTarget::StackUI(u) => u, PointerFocusTarget::WindowUI(u) => u, PointerFocusTarget::ResizeFork(f) => f, @@ -123,6 +152,7 @@ impl PointerFocusTarget { fn inner_touch_target(&self) -> &dyn TouchTarget { match self { PointerFocusTarget::WlSurface { surface, .. } => surface, + PointerFocusTarget::X11Surface { surface, .. } => surface, PointerFocusTarget::StackUI(u) => u, PointerFocusTarget::WindowUI(u) => u, PointerFocusTarget::ResizeFork(f) => f, @@ -148,9 +178,9 @@ impl PointerFocusTarget { ) }), WindowSurface::X11(x11_surface) => Some(( - Self::WlSurface { - surface: x11_surface.wl_surface()?, - toplevel: Some(surface.clone().into()), + Self::X11Surface { + surface: x11_surface.clone(), + toplevel: Some(surface.clone()), }, Point::default(), )), @@ -173,6 +203,10 @@ impl PointerFocusTarget { .find(|(w, _)| w.wl_surface().map(|s2| s == *s2).unwrap_or(false)) .map(|(s, _)| s) }), + PointerFocusTarget::X11Surface { + toplevel: Some(surface), + .. + } => Some(surface.clone()), PointerFocusTarget::StackUI(stack) => Some(stack.active()), PointerFocusTarget::WindowUI(window) => Some(window.surface()), _ => None, @@ -184,6 +218,9 @@ impl PointerFocusTarget { PointerFocusTarget::WlSurface { surface, .. } => { surface.client().is_some_and(|c| c == *client) } + PointerFocusTarget::X11Surface { surface, .. } => surface + .wl_surface() + .is_some_and(|s| s.client().is_some_and(|c| c == *client)), _ => false, } } @@ -263,6 +300,7 @@ impl IsAlive for PointerFocusTarget { match self { // XXX? does this change anything PointerFocusTarget::WlSurface { surface, .. } => surface.alive(), + PointerFocusTarget::X11Surface { surface, .. } => surface.alive(), PointerFocusTarget::StackUI(e) => e.alive(), PointerFocusTarget::WindowUI(e) => e.alive(), PointerFocusTarget::ResizeFork(f) => f.alive(), @@ -459,6 +497,146 @@ impl TouchTarget for PointerFocusTarget { } } +pub enum CosmicOfferData { + Wl(WlOfferData), + X11(XwmOfferData), +} + +impl OfferData for CosmicOfferData { + fn disable(&self) { + match self { + CosmicOfferData::Wl(data) => data.disable(), + CosmicOfferData::X11(data) => data.disable(), + } + } + + fn drop(&self) { + match self { + CosmicOfferData::Wl(data) => data.drop(), + CosmicOfferData::X11(data) => data.drop(), + } + } + + fn validated(&self) -> bool { + match self { + CosmicOfferData::Wl(data) => data.validated(), + CosmicOfferData::X11(data) => data.validated(), + } + } +} + +impl DndFocus for PointerFocusTarget { + type OfferData + = CosmicOfferData + where + S: Source; + + fn enter( + &self, + data: &mut State, + dh: &DisplayHandle, + source: Arc, + seat: &Seat, + location: Point, + serial: &Serial, + ) -> Option> { + match self { + PointerFocusTarget::WlSurface { surface, .. } => { + DndFocus::enter(surface, data, dh, source, seat, location, serial) + .map(CosmicOfferData::Wl) + } + PointerFocusTarget::X11Surface { surface, .. } => { + DndFocus::enter(surface, data, dh, source, seat, location, serial) + .map(CosmicOfferData::X11) + } + _ => None, + } + } + + fn motion( + &self, + data: &mut State, + offer: Option<&mut CosmicOfferData>, + seat: &Seat, + location: Point, + time: u32, + ) { + match self { + PointerFocusTarget::WlSurface { surface, .. } => { + let offer = match offer { + Some(CosmicOfferData::Wl(offer)) => Some(offer), + None => None, + _ => return, + }; + DndFocus::motion(surface, data, offer, seat, location, time) + } + PointerFocusTarget::X11Surface { surface, .. } => { + let offer = match offer { + Some(CosmicOfferData::X11(offer)) => Some(offer), + None => None, + _ => return, + }; + DndFocus::motion(surface, data, offer, seat, location, time) + } + _ => {} + } + } + + fn leave( + &self, + data: &mut State, + offer: Option<&mut CosmicOfferData>, + seat: &Seat, + ) { + match self { + PointerFocusTarget::WlSurface { surface, .. } => { + let offer = match offer { + Some(CosmicOfferData::Wl(offer)) => Some(offer), + None => None, + _ => return, + }; + DndFocus::leave(surface, data, offer, seat) + } + PointerFocusTarget::X11Surface { surface, .. } => { + let offer = match offer { + Some(CosmicOfferData::X11(offer)) => Some(offer), + None => None, + _ => return, + }; + DndFocus::leave(surface, data, offer, seat) + } + _ => {} + } + } + + fn drop( + &self, + data: &mut State, + offer: Option<&mut CosmicOfferData>, + seat: &Seat, + ) { + match self { + PointerFocusTarget::WlSurface { surface, .. } => { + let offer = match offer { + Some(CosmicOfferData::Wl(offer)) => Some(offer), + None => None, + _ => return, + }; + DndFocus::drop(surface, data, offer, seat) + } + PointerFocusTarget::X11Surface { surface, .. } => { + let offer = match offer { + Some(CosmicOfferData::X11(offer)) => Some(offer), + None => None, + _ => return, + }; + DndFocus::drop(surface, data, offer, seat) + } + _ => {} + } + } +} + impl KeyboardTarget for KeyboardFocusTarget { fn enter( &self, @@ -550,6 +728,7 @@ impl WaylandFocus for PointerFocusTarget { fn wl_surface(&self) -> Option> { Some(match self { PointerFocusTarget::WlSurface { surface, .. } => Cow::Borrowed(surface), + PointerFocusTarget::X11Surface { surface, .. } => Cow::Owned(surface.wl_surface()?), PointerFocusTarget::ResizeFork(_) | PointerFocusTarget::StackUI(_) | PointerFocusTarget::WindowUI(_) @@ -561,15 +740,16 @@ impl WaylandFocus for PointerFocusTarget { fn same_client_as(&self, object_id: &ObjectId) -> bool { match self { PointerFocusTarget::WlSurface { surface, .. } => surface.id().same_client_as(object_id), + PointerFocusTarget::X11Surface { surface, .. } => surface + .wl_surface() + .is_some_and(|s| s.id().same_client_as(object_id)), PointerFocusTarget::StackUI(stack) => stack .active() .wl_surface() - .map(|s| s.id().same_client_as(object_id)) - .unwrap_or(false), + .is_some_and(|s| s.id().same_client_as(object_id)), PointerFocusTarget::WindowUI(window) => window .wl_surface() - .map(|s| s.id().same_client_as(object_id)) - .unwrap_or(false), + .is_some_and(|s| s.id().same_client_as(object_id)), PointerFocusTarget::ResizeFork(_) | PointerFocusTarget::ZoomUI(_) => false, } } diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 306df11d..9278be2a 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -908,19 +908,12 @@ impl Workspace { let geometry = self.fullscreen_geometry().unwrap(); return fullscreen .surface - .0 - .surface_under( + .focus_under( (location - geometry.loc.to_f64()).as_logical(), WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE, ) - .map(|(surface, surface_offset)| { - ( - PointerFocusTarget::WlSurface { - surface, - toplevel: Some(fullscreen.surface.clone().into()), - }, - (geometry.loc + surface_offset.as_local()).to_f64(), - ) + .map(|(target, surface_offset)| { + (target, (geometry.loc.to_f64() + surface_offset.as_local())) }); } diff --git a/src/wayland/handlers/data_device.rs b/src/wayland/handlers/data_device.rs index 26aa6cd3..67861874 100644 --- a/src/wayland/handlers/data_device.rs +++ b/src/wayland/handlers/data_device.rs @@ -5,15 +5,14 @@ use smithay::{ delegate_data_device, input::{ Seat, - pointer::{CursorImageStatus, CursorImageSurfaceData}, + dnd::{DnDGrab, DndGrabHandler, DndTarget, GrabType}, + pointer::{CursorImageStatus, CursorImageSurfaceData, Focus}, }, - reexports::wayland_server::protocol::{wl_data_source::WlDataSource, wl_surface::WlSurface}, + reexports::wayland_server::protocol::wl_surface::WlSurface, utils::{IsAlive, Logical, Point}, wayland::{ compositor::{self, SurfaceAttributes}, - selection::data_device::{ - ClientDndGrabHandler, DataDeviceHandler, DataDeviceState, ServerDndGrabHandler, - }, + selection::data_device::{DataDeviceHandler, DataDeviceState, WaylandDndGrabHandler}, }, }; use std::sync::Mutex; @@ -60,12 +59,14 @@ pub fn on_commit(surface: &WlSurface, seat: &Seat) { } } -impl ClientDndGrabHandler for State { - fn started( +impl WaylandDndGrabHandler for State { + fn dnd_requested( &mut self, - _source: Option, + source: S, icon: Option, seat: Seat, + serial: smithay::utils::Serial, + type_: GrabType, ) { let user_data = seat.user_data(); user_data.insert_if_missing_threadsafe::>, _>(Default::default); @@ -89,19 +90,46 @@ impl ClientDndGrabHandler for State { .get::>>() .unwrap() .lock() - .unwrap() = icon.map(|surface| DnDIcon { surface, offset }) - } + .unwrap() = icon.map(|surface| DnDIcon { surface, offset }); - fn dropped(&mut self, _target: Option, _validated: bool, seat: Seat) { - seat.user_data() - .get::>>() - .unwrap() - .lock() - .unwrap() - .take(); + match type_ { + GrabType::Pointer => { + let pointer = seat.get_pointer().unwrap(); + let start_data = pointer.grab_start_data().unwrap(); + pointer.set_grab( + self, + DnDGrab::new_pointer(&self.common.display_handle, start_data, source, seat), + serial, + Focus::Keep, + ); + } + GrabType::Touch => { + let touch = seat.get_touch().unwrap(); + let start_data = touch.grab_start_data().unwrap(); + touch.set_grab( + self, + DnDGrab::new_touch(&self.common.display_handle, start_data, source, seat), + serial, + ); + } + } } } -impl ServerDndGrabHandler for State {} + +impl DndGrabHandler for State { + fn dropped( + &mut self, + _target: Option>, + _validated: bool, + seat: Seat, + _location: Point, + ) { + if let Some(icon) = seat.user_data().get::>>() { + icon.lock().unwrap().take(); + } + } +} + impl DataDeviceHandler for State { fn data_device_state(&mut self) -> &mut DataDeviceState { &mut self.common.data_device_state diff --git a/src/wayland/handlers/selection.rs b/src/wayland/handlers/selection.rs index a2520b72..5abb0b1f 100644 --- a/src/wayland/handlers/selection.rs +++ b/src/wayland/handlers/selection.rs @@ -71,9 +71,7 @@ impl SelectionHandler for State { .as_mut() .and_then(|xstate| xstate.xwm.as_mut()) { - if let Err(err) = - xwm.send_selection(target, mime_type, fd, self.common.event_loop_handle.clone()) - { + if let Err(err) = xwm.send_selection(target, mime_type, fd) { warn!(?err, "Failed to send selection (X11 -> Wayland)."); } } diff --git a/src/xwayland.rs b/src/xwayland.rs index 2e4cfc0b..01c7f2a1 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -114,6 +114,7 @@ impl State { let wm = match X11Wm::start_wm( data.common.event_loop_handle.clone(), + &data.common.display_handle, x11_socket, client.clone(), ) {