From bf4e8cd2337440bb402ea526ea8894fc678964b0 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Wed, 20 Aug 2025 20:31:34 +0300 Subject: [PATCH] wip look into Keyboard/PointerTarget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Decided not to pursue this for now. The idea was to extract input handling from input/mod.rs into the respective modules, plus get rid of manual cursor shape resetting, but there are some roadblocks: - The pointer is locked when those methods are called, meaning you can't get the current location (have to store from enter/motion) and, more crucially, can't set grabs—we need this for the Overview. - Gesture begin/end is handled at the Wayland object level, meaning PointerTargets get to deal with "improper" events themselves, reducing their utility. cosmic-comp spawns an idle callback for this which seems awkward and is asking for problems. - We're not actually removing much code at all, only adding more code for the blanket wrappers and such. --- src/focus.rs | 253 ++++++++++++++++++++++++++++++++++++++ src/handlers/mod.rs | 17 ++- src/handlers/xdg_shell.rs | 34 ++--- src/input/mod.rs | 26 ++-- src/lib.rs | 1 + src/niri.rs | 58 ++++++--- src/ui/screenshot_ui.rs | 139 ++++++++++++++++++++- 7 files changed, 472 insertions(+), 56 deletions(-) create mode 100644 src/focus.rs diff --git a/src/focus.rs b/src/focus.rs new file mode 100644 index 00000000..7a4e2c54 --- /dev/null +++ b/src/focus.rs @@ -0,0 +1,253 @@ +use std::borrow::Cow; + +use smithay::backend::input::KeyState; +use smithay::desktop::PopupKind; +use smithay::input::keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}; +use smithay::input::pointer::{self, PointerTarget}; +use smithay::input::Seat; +use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; +use smithay::utils::{IsAlive, Serial}; +use smithay::wayland::seat::WaylandFocus; + +use crate::niri::State; +use crate::ui::screenshot_ui::ScreenshotUiFocusTarget; + +#[derive(Debug, Clone, PartialEq)] +pub enum KeyboardFocusTarget { + Surface(WlSurface), + ScreenshotUi(ScreenshotUiFocusTarget), + Overview, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum PointerFocusTarget { + Surface(WlSurface), + ScreenshotUi(ScreenshotUiFocusTarget), + Overview, +} + +impl KeyboardFocusTarget { + fn inner(&self) -> &dyn KeyboardTarget { + match self { + Self::Surface(surface) => surface, + Self::ScreenshotUi(x) => x, + Self::Overview => todo!(), + } + } +} + +impl PointerFocusTarget { + fn inner(&self) -> &dyn PointerTarget { + match self { + Self::Surface(surface) => surface, + Self::ScreenshotUi(x) => x, + Self::Overview => todo!(), + } + } + + pub fn surface(&self) -> Option<&WlSurface> { + match self { + PointerFocusTarget::Surface(surface) => Some(surface), + PointerFocusTarget::ScreenshotUi(_) => None, + PointerFocusTarget::Overview => None, + } + } +} + +impl From for PointerFocusTarget { + fn from(value: KeyboardFocusTarget) -> Self { + match value { + KeyboardFocusTarget::Surface(surface) => Self::Surface(surface), + KeyboardFocusTarget::ScreenshotUi(x) => Self::ScreenshotUi(x), + KeyboardFocusTarget::Overview => Self::Overview, + } + } +} + +impl From for KeyboardFocusTarget { + fn from(value: PopupKind) -> Self { + Self::Surface(value.wl_surface().clone()) + } +} + +impl WaylandFocus for KeyboardFocusTarget { + fn wl_surface(&self) -> Option> { + match self { + Self::Surface(surface) => Some(surface), + Self::ScreenshotUi(_) => None, + Self::Overview => None, + } + .map(Cow::Borrowed) + } +} + +impl WaylandFocus for PointerFocusTarget { + fn wl_surface(&self) -> Option> { + self.surface().map(Cow::Borrowed) + } +} + +impl IsAlive for KeyboardFocusTarget { + fn alive(&self) -> bool { + match self { + Self::Surface(surface) => surface.alive(), + Self::ScreenshotUi(_) => true, + Self::Overview => true, + } + } +} + +impl IsAlive for PointerFocusTarget { + fn alive(&self) -> bool { + match self { + Self::Surface(surface) => surface.alive(), + Self::ScreenshotUi(_) => true, + Self::Overview => true, + } + } +} + +impl KeyboardTarget for KeyboardFocusTarget { + fn enter( + &self, + seat: &Seat, + data: &mut State, + keys: Vec>, + serial: Serial, + ) { + self.inner().enter(seat, data, keys, serial); + } + + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial) { + self.inner().leave(seat, data, serial); + } + + fn key( + &self, + seat: &Seat, + data: &mut State, + key: KeysymHandle<'_>, + state: KeyState, + serial: Serial, + time: u32, + ) { + self.inner().key(seat, data, key, state, serial, time); + } + + fn modifiers( + &self, + seat: &Seat, + data: &mut State, + modifiers: ModifiersState, + serial: Serial, + ) { + self.inner().modifiers(seat, data, modifiers, serial); + } +} + +impl PointerTarget for PointerFocusTarget { + fn enter(&self, seat: &Seat, data: &mut State, event: &pointer::MotionEvent) { + self.inner().enter(seat, data, event); + } + + fn motion(&self, seat: &Seat, data: &mut State, event: &pointer::MotionEvent) { + self.inner().motion(seat, data, event); + } + + fn relative_motion( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::RelativeMotionEvent, + ) { + self.inner().relative_motion(seat, data, event); + } + + fn button(&self, seat: &Seat, data: &mut State, event: &pointer::ButtonEvent) { + self.inner().button(seat, data, event); + } + + fn axis(&self, seat: &Seat, data: &mut State, frame: pointer::AxisFrame) { + self.inner().axis(seat, data, frame); + } + + fn frame(&self, seat: &Seat, data: &mut State) { + self.inner().frame(seat, data); + } + + fn gesture_swipe_begin( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::GestureSwipeBeginEvent, + ) { + self.inner().gesture_swipe_begin(seat, data, event); + } + + fn gesture_swipe_update( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::GestureSwipeUpdateEvent, + ) { + self.inner().gesture_swipe_update(seat, data, event); + } + + fn gesture_swipe_end( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::GestureSwipeEndEvent, + ) { + self.inner().gesture_swipe_end(seat, data, event); + } + + fn gesture_pinch_begin( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::GesturePinchBeginEvent, + ) { + self.inner().gesture_pinch_begin(seat, data, event); + } + + fn gesture_pinch_update( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::GesturePinchUpdateEvent, + ) { + self.inner().gesture_pinch_update(seat, data, event); + } + + fn gesture_pinch_end( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::GesturePinchEndEvent, + ) { + self.inner().gesture_pinch_end(seat, data, event); + } + + fn gesture_hold_begin( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::GestureHoldBeginEvent, + ) { + self.inner().gesture_hold_begin(seat, data, event); + } + + fn gesture_hold_end( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::GestureHoldEndEvent, + ) { + self.inner().gesture_hold_end(seat, data, event); + } + + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) { + self.inner().leave(seat, data, serial, time); + } +} diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index 5878e3be..74d869ef 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -40,6 +40,7 @@ use smithay::wayland::keyboard_shortcuts_inhibit::{ }; use smithay::wayland::output::OutputHandler; use smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraintsHandler}; +use smithay::wayland::seat::WaylandFocus; use smithay::wayland::security_context::{ SecurityContext, SecurityContextHandler, SecurityContextListenerSource, }; @@ -75,6 +76,7 @@ use smithay::{ delegate_viewporter, delegate_virtual_keyboard_manager, delegate_xdg_activation, }; +use crate::focus::{KeyboardFocusTarget, PointerFocusTarget}; pub use crate::handlers::xdg_shell::KdeDecorationsModeState; use crate::layout::workspace::WorkspaceId; use crate::layout::ActivateWindow; @@ -102,8 +104,8 @@ use crate::{ pub const XDG_ACTIVATION_TOKEN_TIMEOUT: Duration = Duration::from_secs(10); impl SeatHandler for State { - type KeyboardFocus = WlSurface; - type PointerFocus = WlSurface; + type KeyboardFocus = KeyboardFocusTarget; + type PointerFocus = PointerFocusTarget; type TouchFocus = WlSurface; fn seat_state(&mut self) -> &mut SeatState { @@ -111,19 +113,16 @@ impl SeatHandler for State { } fn cursor_image(&mut self, _seat: &Seat, mut image: CursorImageStatus) { - // FIXME: this hack should be removable once the screenshot UI is tracked with a - // PointerFocus properly. - if self.niri.screenshot_ui.is_open() { - image = CursorImageStatus::Named(CursorIcon::Crosshair); - } self.niri.cursor_manager.set_cursor_image(image); // FIXME: more granular self.niri.queue_redraw_all(); } - fn focus_changed(&mut self, seat: &Seat, focused: Option<&WlSurface>) { + fn focus_changed(&mut self, seat: &Seat, focused: Option<&KeyboardFocusTarget>) { let dh = &self.niri.display_handle; - let client = focused.and_then(|s| dh.get_client(s.id()).ok()); + let client = focused + .and_then(|t| t.wl_surface()) + .and_then(|s| dh.get_client(s.id()).ok()); set_data_device_focus(dh, seat, client.clone()); set_primary_focus(dh, seat, client); } diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index a9eceb67..3540c148 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -38,6 +38,7 @@ use smithay::{ }; use tracing::field::Empty; +use crate::focus::KeyboardFocusTarget; use crate::input::move_grab::MoveGrab; use crate::input::resize_grab::ResizeGrab; use crate::input::touch_move_grab::TouchMoveGrab; @@ -83,14 +84,16 @@ impl XdgShellHandler for State { if grab_serial == serial { let start_data = grab.start_data(); if let Some((focus, _)) = &start_data.focus { - if focus.id().same_client_as(&wl_surface.id()) { - // Deny move requests from DnD grabs to work around - // https://gitlab.gnome.org/GNOME/gtk/-/issues/7113 - let is_dnd_grab = grab.as_any().is::>(); + if let Some(focus) = focus.surface() { + if focus.id().same_client_as(&wl_surface.id()) { + // Deny move requests from DnD grabs to work around + // https://gitlab.gnome.org/GNOME/gtk/-/issues/7113 + let is_dnd_grab = grab.as_any().is::>(); - if !is_dnd_grab { - grab_start_data = - Some(PointerOrTouchStartData::Pointer(start_data.clone())); + if !is_dnd_grab { + grab_start_data = + Some(PointerOrTouchStartData::Pointer(start_data.clone())); + } } } } @@ -182,8 +185,10 @@ impl XdgShellHandler for State { if pointer.has_grab(serial) { if let Some(start_data) = pointer.grab_start_data() { if let Some((focus, _)) = &start_data.focus { - if focus.id().same_client_as(&wl_surface.id()) { - grab_start_data = Some(PointerOrTouchStartData::Pointer(start_data)); + if let Some(focus) = focus.surface() { + if focus.id().same_client_as(&wl_surface.id()) { + grab_start_data = Some(PointerOrTouchStartData::Pointer(start_data)); + } } } } @@ -372,11 +377,12 @@ impl XdgShellHandler for State { } let seat = &self.niri.seat; - let mut grab = match self - .niri - .popups - .grab_popup(root.clone(), popup, seat, serial) - { + let mut grab = match self.niri.popups.grab_popup( + KeyboardFocusTarget::Surface(root.clone()), + popup, + seat, + serial, + ) { Ok(grab) => grab, Err(err) => { trace!("ignoring popup grab: {err:?}"); diff --git a/src/input/mod.rs b/src/input/mod.rs index 711a09dd..eaad8e9f 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -40,6 +40,7 @@ use touch_overview_grab::TouchOverviewGrab; use self::move_grab::MoveGrab; use self::resize_grab::ResizeGrab; use self::spatial_movement_grab::SpatialMovementGrab; +use crate::focus::PointerFocusTarget; use crate::layout::scrolling::ScrollDirection; use crate::layout::{ActivateWindow, LayoutElement as _}; use crate::niri::{CastTarget, PointerVisibility, State}; @@ -626,9 +627,6 @@ impl State { } self.niri.screenshot_ui.close(); - self.niri - .cursor_manager - .set_cursor_image(CursorImageStatus::default_named()); self.niri.queue_redraw_all(); } Action::ScreenshotTogglePointer => { @@ -2152,14 +2150,16 @@ impl State { // // FIXME: ideally this should use the pointer focus with up-to-date global location. let mut pointer_confined = None; - if let Some(under) = &self.niri.pointer_contents.surface { + if let Some(under @ (PointerFocusTarget::Surface(surface), _)) = + &self.niri.pointer_contents.target + { // No need to check if the pointer focus surface matches, because here we're checking // for an already-active constraint, and the constraint is deactivated when the focused // surface changes. let pos_within_surface = pos - under.1; let mut pointer_locked = false; - with_pointer_constraint(&under.0, &pointer, |constraint| { + with_pointer_constraint(surface, &pointer, |constraint| { let Some(constraint) = constraint else { return }; if !constraint.is_active() { return; @@ -2251,7 +2251,7 @@ impl State { let mut prevent = false; // Prevent the pointer from leaving the focused surface. - if Some(&focus_surface.0) != under.surface.as_ref().map(|(s, _)| s) { + if Some(&focus_surface.0) != under.target.as_ref().map(|(s, _)| s) { prevent = true; } @@ -2286,7 +2286,7 @@ impl State { pointer.motion( self, - under.surface.clone(), + under.target.clone(), &MotionEvent { location: new_pos, serial, @@ -2296,7 +2296,7 @@ impl State { pointer.relative_motion( self, - under.surface, + under.target, &RelativeMotionEvent { delta: event.delta(), delta_unaccel: event.delta_unaccel(), @@ -2385,7 +2385,7 @@ impl State { pointer.motion( self, - under.surface, + under.target, &MotionEvent { location: pos, serial, @@ -2761,7 +2761,9 @@ impl State { // updating the pointer contents. pointer .current_focus() - .map(|surface| self.niri.find_root_shell_surface(&surface)) + .as_ref() + .and_then(|target| target.surface()) + .map(|surface| self.niri.find_root_shell_surface(surface)) .map_or(true, |root| { !self .niri @@ -3102,7 +3104,9 @@ impl State { // Get window-specific scroll factor let window_scroll_factor = pointer .current_focus() - .map(|focused| self.niri.find_root_shell_surface(&focused)) + .as_ref() + .and_then(|target| target.surface()) + .map(|focused| self.niri.find_root_shell_surface(focused)) .and_then(|root| self.niri.layout.find_window_and_output(&root).unzip().0) .and_then(|window| window.rules().scroll_factor) .unwrap_or(1.); diff --git a/src/lib.rs b/src/lib.rs index 62d6eebf..50edd313 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ pub mod cli; pub mod cursor; #[cfg(feature = "dbus")] pub mod dbus; +pub mod focus; pub mod frame_clock; pub mod handlers; pub mod input; diff --git a/src/niri.rs b/src/niri.rs index 889705c6..6b46b9ef 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -122,6 +122,7 @@ use crate::dbus::gnome_shell_introspect::{self, IntrospectToNiri, NiriToIntrospe use crate::dbus::gnome_shell_screenshot::{NiriToScreenshot, ScreenshotToNiri}; #[cfg(feature = "xdp-gnome-screencast")] use crate::dbus::mutter_screen_cast::{self, ScreenCastToNiri}; +use crate::focus::{KeyboardFocusTarget, PointerFocusTarget}; use crate::frame_clock::FrameClock; use crate::handlers::{configure_lock_surface, XDG_ACTIVATION_TOKEN_TIMEOUT}; use crate::input::pick_color_grab::PickColorGrab; @@ -161,7 +162,9 @@ use crate::ui::config_error_notification::ConfigErrorNotification; use crate::ui::exit_confirm_dialog::ExitConfirmDialog; use crate::ui::hotkey_overlay::HotkeyOverlay; use crate::ui::screen_transition::{self, ScreenTransition}; -use crate::ui::screenshot_ui::{OutputScreenshot, ScreenshotUi, ScreenshotUiRenderElement}; +use crate::ui::screenshot_ui::{ + OutputScreenshot, ScreenshotUi, ScreenshotUiFocusTarget, ScreenshotUiRenderElement, +}; use crate::utils::scale::{closest_representable_scale, guess_monitor_scale}; use crate::utils::spawning::{CHILD_DISPLAY, CHILD_ENV}; use crate::utils::watcher::Watcher; @@ -524,6 +527,7 @@ pub struct PointContents { // Can be `None` even when `window` is set, for example when the pointer is over the niri // border around the window. pub surface: Option<(WlSurface, Point)>, + pub target: Option<(PointerFocusTarget, Point)>, // If surface belongs to a window, this is that window. pub window: Option<(Window, HitType)>, // If surface belongs to a layer surface, this is that layer surface. @@ -630,6 +634,18 @@ impl KeyboardFocus { pub fn is_overview(&self) -> bool { matches!(self, KeyboardFocus::Overview) } + + pub fn into_target(self) -> Option { + match self { + KeyboardFocus::Layout { surface } => surface.map(KeyboardFocusTarget::Surface), + KeyboardFocus::LayerShell { surface } => Some(KeyboardFocusTarget::Surface(surface)), + KeyboardFocus::LockScreen { surface } => surface.map(KeyboardFocusTarget::Surface), + KeyboardFocus::ScreenshotUi => { + Some(KeyboardFocusTarget::ScreenshotUi(ScreenshotUiFocusTarget)) + } + KeyboardFocus::Overview => Some(KeyboardFocusTarget::Overview), + } + } } pub struct State { @@ -787,7 +803,7 @@ impl State { let pointer = &self.niri.seat.get_pointer().unwrap(); pointer.motion( self, - under.surface, + under.target, &MotionEvent { location, serial: SERIAL_COUNTER.next_serial(), @@ -998,7 +1014,7 @@ impl State { pointer.motion( self, - under.surface, + under.target, &MotionEvent { location, serial: SERIAL_COUNTER.next_serial(), @@ -1276,7 +1292,7 @@ impl State { } self.niri.keyboard_focus.clone_from(&focus); - keyboard.set_focus(self, focus.into_surface(), SERIAL_COUNTER.next_serial()); + keyboard.set_focus(self, focus.into_target(), SERIAL_COUNTER.next_serial()); // FIXME: can be more granular. self.niri.queue_redraw_all(); @@ -1810,9 +1826,6 @@ impl State { .open(renderer, screenshots, default_output, show_pointer) }); - self.niri - .cursor_manager - .set_cursor_image(CursorImageStatus::Named(CursorIcon::Crosshair)); self.niri.queue_redraw_all(); } @@ -1851,9 +1864,6 @@ impl State { }); self.niri.screenshot_ui.close(); - self.niri - .cursor_manager - .set_cursor_image(CursorImageStatus::default_named()); self.niri.queue_redraw_all(); } @@ -2997,8 +3007,6 @@ impl Niri { } if self.screenshot_ui.close() { - self.cursor_manager - .set_cursor_image(CursorImageStatus::default_named()); self.queue_redraw_all(); } } @@ -3044,8 +3052,6 @@ impl Niri { // physical coordinates. if old_size != size || old_scale != scale || old_transform != transform { self.screenshot_ui.close(); - self.cursor_manager - .set_cursor_image(CursorImageStatus::default_named()); self.queue_redraw_all(); return; } @@ -3303,11 +3309,19 @@ impl Niri { (pos_within_output + output_pos_in_global_space).to_f64(), ) }); + rv.target = rv + .surface + .as_ref() + .map(|(surface, pos)| (PointerFocusTarget::Surface(surface.clone()), *pos)); return rv; } if self.screenshot_ui.is_open() { + rv.target = Some(( + PointerFocusTarget::ScreenshotUi(ScreenshotUiFocusTarget), + Point::new(0., 0.), + )); return rv; } @@ -3417,6 +3431,10 @@ impl Niri { .or_else(|| layer_popup_under(Layer::Top)) .or_else(|| layer_toplevel_under(Layer::Top)); + if is_overview_open && under.is_none() { + rv.target = Some((PointerFocusTarget::Overview, Point::new(0., 0.))); + } + under = under.or_else(interactive_moved_window_under); if !is_overview_open { @@ -3445,6 +3463,12 @@ impl Niri { rv.surface = surface_and_pos; rv.window = window; rv.layer = layer; + if rv.target.is_none() { + rv.target = rv + .surface + .as_ref() + .map(|(surface, pos)| (PointerFocusTarget::Surface(surface.clone()), *pos)); + } rv } @@ -5686,8 +5710,6 @@ impl Niri { if self.output_state.is_empty() { // There are no outputs, lock the session right away. self.screenshot_ui.close(); - self.cursor_manager - .set_cursor_image(CursorImageStatus::default_named()); let lock = confirmation.ext_session_lock().clone(); confirmation.lock(); @@ -5746,8 +5768,6 @@ impl Niri { self.event_loop.remove(deadline_token); self.screenshot_ui.close(); - self.cursor_manager - .set_cursor_image(CursorImageStatus::default_named()); if self.output_state.is_empty() { // There are no outputs, lock the session right away. @@ -5893,7 +5913,7 @@ impl Niri { }; let pointer = self.seat.get_pointer().unwrap(); - if Some(surface) != pointer.current_focus().as_ref() { + if Some(surface) != pointer.current_focus().as_ref().and_then(|x| x.surface()) { return; } diff --git a/src/ui/screenshot_ui.rs b/src/ui/screenshot_ui.rs index 3200cae4..6d362e68 100644 --- a/src/ui/screenshot_ui.rs +++ b/src/ui/screenshot_ui.rs @@ -12,17 +12,20 @@ use niri_ipc::SizeChange; use pango::{Alignment, FontDescription}; use pangocairo::cairo::{self, ImageSurface}; use smithay::backend::allocator::Fourcc; -use smithay::backend::input::TouchSlot; +use smithay::backend::input::{KeyState, TouchSlot}; use smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement}; use smithay::backend::renderer::element::Kind; use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture}; use smithay::backend::renderer::{ExportMem, Texture as _}; -use smithay::input::keyboard::{Keysym, ModifiersState}; +use smithay::input::keyboard::{KeyboardTarget, Keysym, KeysymHandle, ModifiersState}; +use smithay::input::pointer::{self, CursorIcon, CursorImageStatus, PointerTarget}; +use smithay::input::{Seat, SeatHandler}; use smithay::output::{Output, WeakOutput}; -use smithay::utils::{Buffer, Physical, Point, Rectangle, Scale, Size, Transform}; +use smithay::utils::{Buffer, IsAlive, Physical, Point, Rectangle, Scale, Serial, Size, Transform}; use crate::animation::{Animation, Clock}; use crate::layout::floating::DIRECTIONAL_MOVE_PX; +use crate::niri::State; use crate::niri_render_elements; use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement; use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement}; @@ -1204,3 +1207,133 @@ fn render_panel( Ok(buffer) } + +#[derive(Debug, Clone, PartialEq)] +pub struct ScreenshotUiFocusTarget; + +impl IsAlive for ScreenshotUiFocusTarget { + fn alive(&self) -> bool { + true + } +} + +impl KeyboardTarget for ScreenshotUiFocusTarget { + fn enter( + &self, + seat: &Seat, + data: &mut State, + keys: Vec>, + serial: Serial, + ) { + } + + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial) {} + + fn key( + &self, + seat: &Seat, + data: &mut State, + key: KeysymHandle<'_>, + state: KeyState, + serial: Serial, + time: u32, + ) { + } + + fn modifiers( + &self, + seat: &Seat, + data: &mut State, + modifiers: ModifiersState, + serial: Serial, + ) { + } +} + +impl PointerTarget for ScreenshotUiFocusTarget { + fn enter(&self, seat: &Seat, data: &mut State, event: &pointer::MotionEvent) { + data.cursor_image(seat, CursorImageStatus::Named(CursorIcon::Crosshair)); + } + + fn motion(&self, seat: &Seat, data: &mut State, event: &pointer::MotionEvent) {} + + fn relative_motion( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::RelativeMotionEvent, + ) { + } + + fn button(&self, seat: &Seat, data: &mut State, event: &pointer::ButtonEvent) {} + + fn axis(&self, seat: &Seat, data: &mut State, frame: pointer::AxisFrame) {} + + fn frame(&self, seat: &Seat, data: &mut State) {} + + fn gesture_swipe_begin( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::GestureSwipeBeginEvent, + ) { + } + + fn gesture_swipe_update( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::GestureSwipeUpdateEvent, + ) { + } + + fn gesture_swipe_end( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::GestureSwipeEndEvent, + ) { + } + + fn gesture_pinch_begin( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::GesturePinchBeginEvent, + ) { + } + + fn gesture_pinch_update( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::GesturePinchUpdateEvent, + ) { + } + + fn gesture_pinch_end( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::GesturePinchEndEvent, + ) { + } + + fn gesture_hold_begin( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::GestureHoldBeginEvent, + ) { + } + + fn gesture_hold_end( + &self, + seat: &Seat, + data: &mut State, + event: &pointer::GestureHoldEndEvent, + ) { + } + + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) {} +}