🔒 Repository is read-only – file editing is disabled.
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)
}
}
}
}