use std::{borrow::Cow, sync::Weak, time::Duration}; use crate::{ shell::{ CosmicSurface, SeatExt, element::{CosmicMapped, CosmicStack, CosmicWindow}, layout::tiling::ResizeForkTarget, zoom::ZoomFocusTarget, }, utils::prelude::*, wayland::handlers::{screencopy::SessionHolder, xdg_shell::popup::get_popup_toplevel}, }; use id_tree::NodeId; use smithay::{ backend::input::KeyState, desktop::{LayerSurface, PopupKind, WindowSurface, WindowSurfaceType, space::SpaceElement}, input::{ Seat, keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, pointer::{ AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent as PointerMotionEvent, PointerTarget, RelativeMotionEvent, }, touch::{ DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget, UpEvent, }, }, reexports::wayland_server::{ Client, Resource, backend::ObjectId, protocol::wl_surface::WlSurface, }, utils::{IsAlive, Logical, Point, Serial, Transform}, wayland::{seat::WaylandFocus, session_lock::LockSurface}, xwayland::{X11Surface, xwm::XwmId}, }; #[derive(Debug, Clone, PartialEq)] pub enum PointerFocusTarget { WlSurface { surface: WlSurface, toplevel: Option, }, StackUI(CosmicStack), WindowUI(CosmicWindow), ResizeFork(ResizeForkTarget), ZoomUI(ZoomFocusTarget), } #[derive(Debug, Clone, PartialEq)] pub enum PointerFocusToplevel { Surface(CosmicSurface), Popup(PopupKind), } impl From for PointerFocusToplevel { fn from(value: CosmicSurface) -> Self { PointerFocusToplevel::Surface(value) } } impl From for PointerFocusToplevel { fn from(value: PopupKind) -> Self { PointerFocusToplevel::Popup(value) } } #[derive(Debug, Clone, PartialEq)] pub enum KeyboardFocusTarget { Element(CosmicMapped), Fullscreen(CosmicSurface), Group(WindowGroup), LayerSurface(LayerSurface), Popup(PopupKind), LockSurface(LockSurface), } // TODO: This should be TryFrom, but PopupGrab needs to be able to convert. Fix this in smithay impl From for PointerFocusTarget { fn from(target: KeyboardFocusTarget) -> Self { 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()), } } 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, }, KeyboardFocusTarget::Popup(popup) => PointerFocusTarget::WlSurface { surface: popup.wl_surface().clone(), toplevel: Some(popup.into()), }, KeyboardFocusTarget::LockSurface(lock) => PointerFocusTarget::WlSurface { surface: lock.wl_surface().clone(), toplevel: None, }, _ => unreachable!("A group cannot start a popup grab"), } } } impl PointerFocusTarget { fn inner_pointer_target(&self) -> &dyn PointerTarget { match self { PointerFocusTarget::WlSurface { surface, .. } => surface, PointerFocusTarget::StackUI(u) => u, PointerFocusTarget::WindowUI(u) => u, PointerFocusTarget::ResizeFork(f) => f, PointerFocusTarget::ZoomUI(e) => e, } } fn inner_touch_target(&self) -> &dyn TouchTarget { match self { PointerFocusTarget::WlSurface { 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, ) -> Option<(Self, Point)> { match surface.0.underlying_surface() { WindowSurface::Wayland(_toplevel) => surface .0 .surface_under(point, WindowSurfaceType::ALL) .map(|(wl_surface, point)| { ( Self::WlSurface { surface: wl_surface, toplevel: Some(surface.clone().into()), }, point, ) }), WindowSurface::X11(x11_surface) => Some(( Self::WlSurface { surface: x11_surface.wl_surface()?, toplevel: Some(surface.clone().into()), }, Point::default(), )), } } pub fn toplevel(&self, shell: &Shell) -> Option { match &self { PointerFocusTarget::WlSurface { toplevel: Some(PointerFocusToplevel::Surface(surface)), .. } => Some(surface.clone()), PointerFocusTarget::WlSurface { toplevel: Some(PointerFocusToplevel::Popup(PopupKind::Xdg(popup))), .. } => get_popup_toplevel(popup) .and_then(|s| shell.element_for_surface(&s).map(|mapped| (mapped, s))) .and_then(|(m, s)| { m.windows() .find(|(w, _)| w.wl_surface().map(|s2| s == *s2).unwrap_or(false)) .map(|(s, _)| s) }), PointerFocusTarget::StackUI(stack) => Some(stack.active()), PointerFocusTarget::WindowUI(window) => Some(window.surface()), _ => None, } } pub fn is_client(&self, client: &Client) -> bool { match self { PointerFocusTarget::WlSurface { surface, .. } => { surface.client().is_some_and(|c| c == *client) } _ => false, } } } impl KeyboardFocusTarget { fn inner_keyboard_target(&self) -> Option<&dyn KeyboardTarget> { match self { KeyboardFocusTarget::Element(w) => Some(w), KeyboardFocusTarget::Fullscreen(w) => Some(w), KeyboardFocusTarget::Group(_) => None, KeyboardFocusTarget::LayerSurface(l) => Some(l.wl_surface()), KeyboardFocusTarget::Popup(p) => Some(p.wl_surface()), KeyboardFocusTarget::LockSurface(l) => Some(l.wl_surface()), } } pub fn toplevel(&self) -> Option> { match self { KeyboardFocusTarget::Element(mapped) => mapped.wl_surface(), KeyboardFocusTarget::Popup(PopupKind::Xdg(xdg)) => { get_popup_toplevel(xdg).map(Cow::Owned) } _ => None, } } pub fn windows(&self) -> impl Iterator + '_ { match self { KeyboardFocusTarget::Element(mapped) => Box::new(mapped.windows().map(|(s, _)| s)) as Box>, KeyboardFocusTarget::Fullscreen(surface) => Box::new(std::iter::once(surface.clone())), _ => Box::new(std::iter::empty()), } } pub fn active_window(&self) -> Option { match self { KeyboardFocusTarget::Element(mapped) => Some(mapped.active_window()), KeyboardFocusTarget::Fullscreen(surface) => Some(surface.clone()), _ => None, } } fn x11_surface(&self) -> Option { match self { KeyboardFocusTarget::Element(mapped) => mapped.active_window().x11_surface().cloned(), KeyboardFocusTarget::Fullscreen(surface) => surface.x11_surface().cloned(), _ => None, } } pub fn is_xwm(&self, xwm: XwmId) -> bool { if let Some(surface) = self.x11_surface() { surface.xwm_id().unwrap() == xwm } else { false } } } #[derive(Debug, Clone)] pub struct WindowGroup { pub node: NodeId, pub alive: Weak<()>, pub focus_stack: Vec, } impl PartialEq for WindowGroup { fn eq(&self, other: &Self) -> bool { self.node == other.node && Weak::ptr_eq(&self.alive, &other.alive) } } impl IsAlive for PointerFocusTarget { fn alive(&self) -> bool { match self { // XXX? does this change anything PointerFocusTarget::WlSurface { surface, .. } => surface.alive(), PointerFocusTarget::StackUI(e) => e.alive(), PointerFocusTarget::WindowUI(e) => e.alive(), PointerFocusTarget::ResizeFork(f) => f.alive(), PointerFocusTarget::ZoomUI(_) => true, } } } impl IsAlive for KeyboardFocusTarget { fn alive(&self) -> bool { match self { KeyboardFocusTarget::Element(e) => e.alive(), KeyboardFocusTarget::Fullscreen(f) => f.alive(), KeyboardFocusTarget::Group(g) => g.alive.upgrade().is_some(), KeyboardFocusTarget::LayerSurface(l) => l.alive(), KeyboardFocusTarget::Popup(p) => p.alive(), KeyboardFocusTarget::LockSurface(l) => l.alive(), } } } impl PointerTarget for PointerFocusTarget { fn enter(&self, seat: &Seat, data: &mut State, event: &PointerMotionEvent) { let toplevel = self.toplevel(&data.common.shell.read()); if let Some(element) = toplevel { for session in element.cursor_sessions() { session.set_cursor_pos(Some( event .location .to_buffer(1.0, Transform::Normal, &element.geometry().size.to_f64()) .to_i32_round(), )); if let Some((_, hotspot)) = seat .cursor_geometry((0.0, 0.0), Duration::from_millis(event.time as u64).into()) { session.set_cursor_hotspot(hotspot); } else { session.set_cursor_hotspot((0, 0)); } } } self.inner_pointer_target().enter(seat, data, event); } fn motion(&self, seat: &Seat, data: &mut State, event: &PointerMotionEvent) { let toplevel = self.toplevel(&data.common.shell.read()); if let Some(element) = toplevel { for session in element.cursor_sessions() { session.set_cursor_pos(Some( event .location .to_buffer(1.0, Transform::Normal, &element.geometry().size.to_f64()) .to_i32_round(), )); if let Some((_, hotspot)) = seat .cursor_geometry((0.0, 0.0), Duration::from_millis(event.time as u64).into()) { session.set_cursor_hotspot(hotspot); } else { session.set_cursor_hotspot((0, 0)); } } } self.inner_pointer_target().motion(seat, data, event); } fn relative_motion(&self, seat: &Seat, data: &mut State, event: &RelativeMotionEvent) { self.inner_pointer_target() .relative_motion(seat, data, event); } fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) { self.inner_pointer_target().button(seat, data, event); } fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) { self.inner_pointer_target().axis(seat, data, frame); } 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) { let toplevel = self.toplevel(&data.common.shell.read()); if let Some(element) = toplevel { for session in element.cursor_sessions() { session.set_cursor_pos(None); session.set_cursor_hotspot((0, 0)); } } self.inner_pointer_target().leave(seat, data, serial, time); } fn gesture_swipe_begin( &self, seat: &Seat, data: &mut State, event: &GestureSwipeBeginEvent, ) { self.inner_pointer_target() .gesture_swipe_begin(seat, data, event); } fn gesture_swipe_update( &self, seat: &Seat, data: &mut State, event: &GestureSwipeUpdateEvent, ) { self.inner_pointer_target() .gesture_swipe_update(seat, data, event); } fn gesture_swipe_end( &self, seat: &Seat, data: &mut State, event: &GestureSwipeEndEvent, ) { self.inner_pointer_target() .gesture_swipe_end(seat, data, event); } fn gesture_pinch_begin( &self, seat: &Seat, data: &mut State, event: &GesturePinchBeginEvent, ) { self.inner_pointer_target() .gesture_pinch_begin(seat, data, event); } fn gesture_pinch_update( &self, seat: &Seat, data: &mut State, event: &GesturePinchUpdateEvent, ) { self.inner_pointer_target() .gesture_pinch_update(seat, data, event); } fn gesture_pinch_end( &self, seat: &Seat, data: &mut State, event: &GesturePinchEndEvent, ) { self.inner_pointer_target() .gesture_pinch_end(seat, data, event); } fn gesture_hold_begin( &self, seat: &Seat, data: &mut State, event: &GestureHoldBeginEvent, ) { self.inner_pointer_target() .gesture_hold_begin(seat, data, event); } fn gesture_hold_end(&self, seat: &Seat, data: &mut State, event: &GestureHoldEndEvent) { self.inner_pointer_target() .gesture_hold_end(seat, data, event); } } impl TouchTarget for PointerFocusTarget { fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent, seq: Serial) { self.inner_touch_target().down(seat, data, event, seq); } fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent, seq: Serial) { self.inner_touch_target().up(seat, data, event, seq); } fn motion(&self, seat: &Seat, data: &mut State, event: &TouchMotionEvent, seq: Serial) { self.inner_touch_target().motion(seat, data, event, seq); } fn frame(&self, seat: &Seat, data: &mut State, seq: Serial) { self.inner_touch_target().frame(seat, data, seq); } fn cancel(&self, seat: &Seat, data: &mut State, seq: Serial) { self.inner_touch_target().cancel(seat, data, seq); } fn shape(&self, seat: &Seat, data: &mut State, event: &ShapeEvent, seq: Serial) { self.inner_touch_target().shape(seat, data, event, seq); } fn orientation( &self, seat: &Seat, data: &mut State, event: &OrientationEvent, seq: Serial, ) { self.inner_touch_target() .orientation(seat, data, event, seq); } } impl KeyboardTarget for KeyboardFocusTarget { fn enter( &self, seat: &Seat, data: &mut State, keys: Vec>, serial: Serial, ) { if let Some(inner) = self.inner_keyboard_target() { inner.enter(seat, data, keys, serial); } } fn leave(&self, seat: &Seat, data: &mut State, serial: Serial) { if let Some(inner) = self.inner_keyboard_target() { inner.leave(seat, data, serial); } } fn key( &self, seat: &Seat, data: &mut State, key: KeysymHandle<'_>, state: KeyState, serial: Serial, time: u32, ) { if let Some(inner) = self.inner_keyboard_target() { inner.key(seat, data, key, state, serial, time); } } fn modifiers( &self, seat: &Seat, data: &mut State, modifiers: ModifiersState, serial: Serial, ) { if let Some(inner) = self.inner_keyboard_target() { inner.modifiers(seat, data, modifiers, serial); } } fn replace( &self, replaced: Self, seat: &Seat, data: &mut State, keys: Vec>, modifiers: ModifiersState, serial: Serial, ) { if !replaced .wl_surface() .is_some_and(|s| Some(s) == self.wl_surface()) && !replaced .x11_surface() .is_some_and(|s| Some(s) == self.x11_surface()) { KeyboardTarget::leave(&replaced, seat, data, serial); KeyboardTarget::enter(self, seat, data, keys, serial); KeyboardTarget::modifiers(self, seat, data, modifiers, serial); } } } impl WaylandFocus for KeyboardFocusTarget { fn wl_surface(&self) -> Option> { match self { KeyboardFocusTarget::Element(w) => WaylandFocus::wl_surface(w), KeyboardFocusTarget::Fullscreen(w) => WaylandFocus::wl_surface(w), KeyboardFocusTarget::Group(_) => None, KeyboardFocusTarget::LayerSurface(l) => Some(Cow::Borrowed(l.wl_surface())), KeyboardFocusTarget::Popup(p) => Some(Cow::Borrowed(p.wl_surface())), KeyboardFocusTarget::LockSurface(l) => Some(Cow::Borrowed(l.wl_surface())), } } fn same_client_as(&self, object_id: &ObjectId) -> bool { match self { KeyboardFocusTarget::Element(w) => WaylandFocus::same_client_as(w, object_id), KeyboardFocusTarget::Fullscreen(w) => WaylandFocus::same_client_as(w, object_id), KeyboardFocusTarget::Group(_) => false, KeyboardFocusTarget::LayerSurface(l) => l.wl_surface().id().same_client_as(object_id), KeyboardFocusTarget::Popup(p) => p.wl_surface().id().same_client_as(object_id), KeyboardFocusTarget::LockSurface(l) => l.wl_surface().id().same_client_as(object_id), } } } impl WaylandFocus for PointerFocusTarget { fn wl_surface(&self) -> Option> { Some(match self { PointerFocusTarget::WlSurface { surface, .. } => Cow::Borrowed(surface), PointerFocusTarget::ResizeFork(_) | PointerFocusTarget::StackUI(_) | PointerFocusTarget::WindowUI(_) | PointerFocusTarget::ZoomUI(_) => { return None; } }) } fn same_client_as(&self, object_id: &ObjectId) -> bool { match self { PointerFocusTarget::WlSurface { surface, .. } => surface.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), PointerFocusTarget::WindowUI(window) => window .wl_surface() .map(|s| s.id().same_client_as(object_id)) .unwrap_or(false), PointerFocusTarget::ResizeFork(_) | PointerFocusTarget::ZoomUI(_) => false, } } } impl From for PointerFocusTarget { fn from(p: PopupKind) -> Self { PointerFocusTarget::WlSurface { surface: p.wl_surface().clone(), toplevel: None, } } } impl From for PointerFocusTarget { fn from(f: ResizeForkTarget) -> Self { PointerFocusTarget::ResizeFork(f) } } impl From for PointerFocusTarget { fn from(l: LockSurface) -> Self { PointerFocusTarget::WlSurface { surface: l.wl_surface().clone(), toplevel: None, } } } impl From for KeyboardFocusTarget { fn from(w: CosmicMapped) -> Self { KeyboardFocusTarget::Element(w) } } impl From for KeyboardFocusTarget { fn from(s: CosmicSurface) -> Self { KeyboardFocusTarget::Fullscreen(s) } } impl From for KeyboardFocusTarget { fn from(w: WindowGroup) -> Self { KeyboardFocusTarget::Group(w) } } impl From for KeyboardFocusTarget { fn from(l: LayerSurface) -> Self { KeyboardFocusTarget::LayerSurface(l) } } impl From for KeyboardFocusTarget { fn from(p: PopupKind) -> Self { KeyboardFocusTarget::Popup(p) } } impl From for KeyboardFocusTarget { fn from(l: LockSurface) -> Self { KeyboardFocusTarget::LockSurface(l) } }