🔒 Repository is read-only – file editing is disabled.

PaganDE/pagan-compositor/src/focus.rs main

518 linii Raw ← Powrót
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
//! 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<PointerFocusTarget> 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<WlSurface> 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<PopupKind> 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<KeyboardFocus>`).
// Dla okna bierzemy jego powierzchnię toplevela.
impl From<KeyboardFocusTarget> 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<Window> for KeyboardFocusTarget {
    fn from(value: Window) -> Self {
        KeyboardFocusTarget::Window(value)
    }
}

impl From<PopupKind> for KeyboardFocusTarget {
    fn from(value: PopupKind) -> Self {
        KeyboardFocusTarget::Popup(value)
    }
}

impl WaylandFocus for PointerFocusTarget {
    fn wl_surface(&self) -> Option<Cow<'_, WlSurface>> {
        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<Cow<'_, WlSurface>> {
        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<MyCompositor> for PointerFocusTarget {
    fn enter(&self, seat: &Seat<MyCompositor>, 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<MyCompositor>, 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<MyCompositor>,
        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<MyCompositor>, 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<MyCompositor>, 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<MyCompositor>, 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<MyCompositor>, 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<MyCompositor>,
        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<MyCompositor>,
        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<MyCompositor>,
        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<MyCompositor>,
        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<MyCompositor>,
        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<MyCompositor>,
        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<MyCompositor>,
        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<MyCompositor>,
        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<MyCompositor> for KeyboardFocusTarget {
    fn enter(
        &self,
        seat: &Seat<MyCompositor>,
        data: &mut MyCompositor,
        keys: Vec<KeysymHandle<'_>>,
        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<MyCompositor>, 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<MyCompositor>,
        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<MyCompositor>,
        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<MyCompositor> for PointerFocusTarget {
    fn down(
        &self,
        seat: &Seat<MyCompositor>,
        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<MyCompositor>, 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<MyCompositor>,
        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<MyCompositor>, 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<MyCompositor>, 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<MyCompositor>,
        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<MyCompositor>,
        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)
            }
        }
    }
}