//! Cele fokusa (focus targets) dla myszy i klawiatury. //! //! PO CO osobny moduł: Smithay nie wie, czym jest "okno" w naszym kompozytorze. //! Żeby przekazać zdarzenia wejścia do właściwego klienta, musimy wskazać cel, //! który implementuje odpowiednie traity Smithay: //! - `PointerTarget` – gdzie trafiają ruch/klik/scroll, //! - `KeyboardTarget` – kto dostaje wciśnięcia klawiszy, //! - `TouchTarget` – dotyk, //! - `WaylandFocus` – powiązanie celu z konkretną `wl_surface` (do wysyłki). //! //! Kluczowa decyzja architektoniczna (hybrydowe CSD): skoro klient rysuje CAŁY //! pasek tytułu i przyciski, to zdarzenia myszy muszą trafiać BEZPOŚREDNIO w //! jego `wl_surface` — kompozytor nie ma tu żadnej własnej dekoracji do //! obsłużenia. Dlatego `PointerFocusTarget` to głównie `WlSurface`. use std::borrow::Cow; use smithay::input::touch::{ DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, UpEvent, }; pub use smithay::{ backend::input::KeyState, desktop::{PopupKind, Window, WindowSurface}, input::{ keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, pointer::{ AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent, PointerTarget, RelativeMotionEvent, }, touch::TouchTarget, Seat, }, reexports::wayland_server::protocol::wl_surface::WlSurface, utils::{IsAlive, Serial}, wayland::seat::WaylandFocus, }; use crate::state::MyCompositor; /// Cel fokusa klawiatury. Dla okien z hybrydowym CSD jest to całe okno — jego /// powierzchnia kliencka i tak obsługuje treść, a my nie mamy własnych /// dekoracji wymagających osobnego celu. #[derive(Debug, Clone, PartialEq)] pub enum KeyboardFocusTarget { /// Zwykłe okno aplikacji (toplevel). Window(Window), /// Popup (np. rozwijane menu), który przechwytuje klawiaturę gdy jest otwarty. Popup(PopupKind), } impl IsAlive for KeyboardFocusTarget { fn alive(&self) -> bool { match self { KeyboardFocusTarget::Window(w) => w.alive(), KeyboardFocusTarget::Popup(p) => p.alive(), } } } /// Cel fokusa wskaźnika. Świadomie operujemy na `WlSurface` (a nie na oknie): /// przy CSD klient sam decyduje, co znaczy klik w danym miejscu (pasek tytułu, /// przycisk, krawędź). Kompozytor nie zgaduje — dostarcza zdarzenie do klienta. #[derive(Debug, Clone, PartialEq)] pub enum PointerFocusTarget { /// Powierzchnia kliencka (treść okna + jego własne dekoracje). WlSurface(WlSurface), /// Popup (np. menu kontekstowe). Popup(PopupKind), } impl IsAlive for PointerFocusTarget { fn alive(&self) -> bool { match self { PointerFocusTarget::WlSurface(w) => w.alive(), PointerFocusTarget::Popup(p) => p.alive(), } } } impl From for WlSurface { fn from(target: PointerFocusTarget) -> Self { // Bezpieczne, bo każdy wariant ma powierzchnię; wywołujący wcześniej // sprawdza `alive()`. target.wl_surface().unwrap().into_owned() } } impl From for PointerFocusTarget { fn from(value: WlSurface) -> Self { PointerFocusTarget::WlSurface(value) } } impl From<&WlSurface> for PointerFocusTarget { fn from(value: &WlSurface) -> Self { PointerFocusTarget::WlSurface(value.clone()) } } impl From for PointerFocusTarget { fn from(value: PopupKind) -> Self { PointerFocusTarget::Popup(value) } } // Zamiana celu klawiatury na cel wskaźnika. Wymaga tego Smithay przy grabach // popupów (`PopupManager::grab_popup` wymaga `PointerFocus: From`). // Dla okna bierzemy jego powierzchnię toplevela. impl From for PointerFocusTarget { fn from(target: KeyboardFocusTarget) -> Self { match target { KeyboardFocusTarget::Window(window) => match window.underlying_surface() { WindowSurface::Wayland(surface) => { PointerFocusTarget::WlSurface(surface.wl_surface().clone()) } // Okno X11 ma powiązaną `wl_surface` (XWayland tworzy ją przy // asocjacji, zanim okno zostanie zarządzane), więc konwersja jest // zawsze wykonalna. `expect` jak `unreachable!()` w anvilu. WindowSurface::X11(surface) => PointerFocusTarget::WlSurface( surface .wl_surface() .expect("okno X11 bez powiązanej wl_surface"), ), }, KeyboardFocusTarget::Popup(popup) => PointerFocusTarget::Popup(popup), } } } impl From for KeyboardFocusTarget { fn from(value: Window) -> Self { KeyboardFocusTarget::Window(value) } } impl From for KeyboardFocusTarget { fn from(value: PopupKind) -> Self { KeyboardFocusTarget::Popup(value) } } impl WaylandFocus for PointerFocusTarget { fn wl_surface(&self) -> Option> { match self { PointerFocusTarget::WlSurface(w) => Some(Cow::Borrowed(w)), PointerFocusTarget::Popup(p) => Some(Cow::Borrowed(p.wl_surface())), } } } impl WaylandFocus for KeyboardFocusTarget { fn wl_surface(&self) -> Option> { match self { KeyboardFocusTarget::Window(w) => match w.underlying_surface() { WindowSurface::Wayland(surface) => Some(Cow::Borrowed(surface.wl_surface())), WindowSurface::X11(surface) => surface.wl_surface().map(Cow::Owned), }, KeyboardFocusTarget::Popup(p) => Some(Cow::Borrowed(p.wl_surface())), } } } /// Delegacja zdarzeń wskaźnika do konkretnej powierzchni Waylanda. /// Smithay dostarcza implementacje `PointerTarget` dla `WlSurface`, więc /// wystarczy przekierować. impl PointerTarget for PointerFocusTarget { fn enter(&self, seat: &Seat, data: &mut MyCompositor, event: &MotionEvent) { match self { PointerFocusTarget::WlSurface(w) => PointerTarget::enter(w, seat, data, event), PointerFocusTarget::Popup(p) => PointerTarget::enter(p.wl_surface(), seat, data, event), } } fn motion(&self, seat: &Seat, data: &mut MyCompositor, event: &MotionEvent) { match self { PointerFocusTarget::WlSurface(w) => PointerTarget::motion(w, seat, data, event), PointerFocusTarget::Popup(p) => { PointerTarget::motion(p.wl_surface(), seat, data, event) } } } fn relative_motion( &self, seat: &Seat, data: &mut MyCompositor, event: &RelativeMotionEvent, ) { match self { PointerFocusTarget::WlSurface(w) => { PointerTarget::relative_motion(w, seat, data, event) } PointerFocusTarget::Popup(p) => { PointerTarget::relative_motion(p.wl_surface(), seat, data, event) } } } fn button(&self, seat: &Seat, data: &mut MyCompositor, event: &ButtonEvent) { match self { PointerFocusTarget::WlSurface(w) => PointerTarget::button(w, seat, data, event), PointerFocusTarget::Popup(p) => { PointerTarget::button(p.wl_surface(), seat, data, event) } } } fn axis(&self, seat: &Seat, data: &mut MyCompositor, frame: AxisFrame) { match self { PointerFocusTarget::WlSurface(w) => PointerTarget::axis(w, seat, data, frame), PointerFocusTarget::Popup(p) => PointerTarget::axis(p.wl_surface(), seat, data, frame), } } fn frame(&self, seat: &Seat, data: &mut MyCompositor) { match self { PointerFocusTarget::WlSurface(w) => PointerTarget::frame(w, seat, data), PointerFocusTarget::Popup(p) => PointerTarget::frame(p.wl_surface(), seat, data), } } fn leave(&self, seat: &Seat, data: &mut MyCompositor, serial: Serial, time: u32) { match self { PointerFocusTarget::WlSurface(w) => PointerTarget::leave(w, seat, data, serial, time), PointerFocusTarget::Popup(p) => { PointerTarget::leave(p.wl_surface(), seat, data, serial, time) } } } fn gesture_swipe_begin( &self, seat: &Seat, data: &mut MyCompositor, event: &GestureSwipeBeginEvent, ) { match self { PointerFocusTarget::WlSurface(w) => { PointerTarget::gesture_swipe_begin(w, seat, data, event) } PointerFocusTarget::Popup(p) => { PointerTarget::gesture_swipe_begin(p.wl_surface(), seat, data, event) } } } fn gesture_swipe_update( &self, seat: &Seat, data: &mut MyCompositor, event: &GestureSwipeUpdateEvent, ) { match self { PointerFocusTarget::WlSurface(w) => { PointerTarget::gesture_swipe_update(w, seat, data, event) } PointerFocusTarget::Popup(p) => { PointerTarget::gesture_swipe_update(p.wl_surface(), seat, data, event) } } } fn gesture_swipe_end( &self, seat: &Seat, data: &mut MyCompositor, event: &GestureSwipeEndEvent, ) { match self { PointerFocusTarget::WlSurface(w) => { PointerTarget::gesture_swipe_end(w, seat, data, event) } PointerFocusTarget::Popup(p) => { PointerTarget::gesture_swipe_end(p.wl_surface(), seat, data, event) } } } fn gesture_pinch_begin( &self, seat: &Seat, data: &mut MyCompositor, event: &GesturePinchBeginEvent, ) { match self { PointerFocusTarget::WlSurface(w) => { PointerTarget::gesture_pinch_begin(w, seat, data, event) } PointerFocusTarget::Popup(p) => { PointerTarget::gesture_pinch_begin(p.wl_surface(), seat, data, event) } } } fn gesture_pinch_update( &self, seat: &Seat, data: &mut MyCompositor, event: &GesturePinchUpdateEvent, ) { match self { PointerFocusTarget::WlSurface(w) => { PointerTarget::gesture_pinch_update(w, seat, data, event) } PointerFocusTarget::Popup(p) => { PointerTarget::gesture_pinch_update(p.wl_surface(), seat, data, event) } } } fn gesture_pinch_end( &self, seat: &Seat, data: &mut MyCompositor, event: &GesturePinchEndEvent, ) { match self { PointerFocusTarget::WlSurface(w) => { PointerTarget::gesture_pinch_end(w, seat, data, event) } PointerFocusTarget::Popup(p) => { PointerTarget::gesture_pinch_end(p.wl_surface(), seat, data, event) } } } fn gesture_hold_begin( &self, seat: &Seat, data: &mut MyCompositor, event: &GestureHoldBeginEvent, ) { match self { PointerFocusTarget::WlSurface(w) => { PointerTarget::gesture_hold_begin(w, seat, data, event) } PointerFocusTarget::Popup(p) => { PointerTarget::gesture_hold_begin(p.wl_surface(), seat, data, event) } } } fn gesture_hold_end( &self, seat: &Seat, data: &mut MyCompositor, event: &GestureHoldEndEvent, ) { match self { PointerFocusTarget::WlSurface(w) => { PointerTarget::gesture_hold_end(w, seat, data, event) } PointerFocusTarget::Popup(p) => { PointerTarget::gesture_hold_end(p.wl_surface(), seat, data, event) } } } } /// Delegacja zdarzeń klawiatury. Dla okna używamy powierzchni toplevela. impl KeyboardTarget for KeyboardFocusTarget { fn enter( &self, seat: &Seat, data: &mut MyCompositor, keys: Vec>, serial: Serial, ) { match self { KeyboardFocusTarget::Window(w) => match w.underlying_surface() { WindowSurface::Wayland(w) => { KeyboardTarget::enter(w.wl_surface(), seat, data, keys, serial) } WindowSurface::X11(surface) => { if let Some(wl) = surface.wl_surface() { KeyboardTarget::enter(&wl, seat, data, keys, serial) } } }, KeyboardFocusTarget::Popup(p) => { KeyboardTarget::enter(p.wl_surface(), seat, data, keys, serial) } } } fn leave(&self, seat: &Seat, data: &mut MyCompositor, serial: Serial) { match self { KeyboardFocusTarget::Window(w) => match w.underlying_surface() { WindowSurface::Wayland(w) => { KeyboardTarget::leave(w.wl_surface(), seat, data, serial) } WindowSurface::X11(surface) => { if let Some(wl) = surface.wl_surface() { KeyboardTarget::leave(&wl, seat, data, serial) } } }, KeyboardFocusTarget::Popup(p) => { KeyboardTarget::leave(p.wl_surface(), seat, data, serial) } } } fn key( &self, seat: &Seat, data: &mut MyCompositor, key: KeysymHandle<'_>, state: KeyState, serial: Serial, time: u32, ) { match self { KeyboardFocusTarget::Window(w) => match w.underlying_surface() { WindowSurface::Wayland(w) => { KeyboardTarget::key(w.wl_surface(), seat, data, key, state, serial, time) } WindowSurface::X11(surface) => { if let Some(wl) = surface.wl_surface() { KeyboardTarget::key(&wl, seat, data, key, state, serial, time) } } }, KeyboardFocusTarget::Popup(p) => { KeyboardTarget::key(p.wl_surface(), seat, data, key, state, serial, time) } } } fn modifiers( &self, seat: &Seat, data: &mut MyCompositor, modifiers: ModifiersState, serial: Serial, ) { match self { KeyboardFocusTarget::Window(w) => match w.underlying_surface() { WindowSurface::Wayland(w) => { KeyboardTarget::modifiers(w.wl_surface(), seat, data, modifiers, serial) } WindowSurface::X11(surface) => { if let Some(wl) = surface.wl_surface() { KeyboardTarget::modifiers(&wl, seat, data, modifiers, serial) } } }, KeyboardFocusTarget::Popup(p) => { KeyboardTarget::modifiers(p.wl_surface(), seat, data, modifiers, serial) } } } } /// Delegacja dotyku — w tym szkieletie nie dodajemy jeszcze urządzeń /// dotykowych, ale trait musi być spełniony, więc przekierowujemy do /// powierzchni klienckiej (poprawne zachowanie, gdy dotyk pojawi się później). impl TouchTarget for PointerFocusTarget { fn down( &self, seat: &Seat, data: &mut MyCompositor, event: &DownEvent, seq: Serial, ) { match self { PointerFocusTarget::WlSurface(w) => TouchTarget::down(w, seat, data, event, seq), PointerFocusTarget::Popup(p) => { TouchTarget::down(p.wl_surface(), seat, data, event, seq) } } } fn up(&self, seat: &Seat, data: &mut MyCompositor, event: &UpEvent, seq: Serial) { match self { PointerFocusTarget::WlSurface(w) => TouchTarget::up(w, seat, data, event, seq), PointerFocusTarget::Popup(p) => TouchTarget::up(p.wl_surface(), seat, data, event, seq), } } fn motion( &self, seat: &Seat, data: &mut MyCompositor, event: &TouchMotionEvent, seq: Serial, ) { match self { PointerFocusTarget::WlSurface(w) => TouchTarget::motion(w, seat, data, event, seq), PointerFocusTarget::Popup(p) => { TouchTarget::motion(p.wl_surface(), seat, data, event, seq) } } } fn frame(&self, seat: &Seat, data: &mut MyCompositor, seq: Serial) { match self { PointerFocusTarget::WlSurface(w) => TouchTarget::frame(w, seat, data, seq), PointerFocusTarget::Popup(p) => TouchTarget::frame(p.wl_surface(), seat, data, seq), } } fn cancel(&self, seat: &Seat, data: &mut MyCompositor, seq: Serial) { match self { PointerFocusTarget::WlSurface(w) => TouchTarget::cancel(w, seat, data, seq), PointerFocusTarget::Popup(p) => TouchTarget::cancel(p.wl_surface(), seat, data, seq), } } fn shape( &self, seat: &Seat, data: &mut MyCompositor, event: &ShapeEvent, seq: Serial, ) { match self { PointerFocusTarget::WlSurface(w) => TouchTarget::shape(w, seat, data, event, seq), PointerFocusTarget::Popup(p) => { TouchTarget::shape(p.wl_surface(), seat, data, event, seq) } } } fn orientation( &self, seat: &Seat, data: &mut MyCompositor, event: &OrientationEvent, seq: Serial, ) { match self { PointerFocusTarget::WlSurface(w) => TouchTarget::orientation(w, seat, data, event, seq), PointerFocusTarget::Popup(p) => { TouchTarget::orientation(p.wl_surface(), seat, data, event, seq) } } } }