feat(mirada): puntero/ratón en el backend DRM del compositor

El backend DRM del Cuerpo deja de ser sólo-teclado: `libinput` ahora
mueve un cursor de software y reenvía clics y rueda a los clientes.

- Enum `Frame` (vía `render_elements!`) que mezcla superficies de
  cliente y un `SolidColorRenderElement` para el cursor, marcado
  `Kind::Cursor` y compuesto encima de todo.
- `handle_input` atiende `PointerMotion`/`PointerMotionAbsolute`/
  `PointerButton`/`PointerAxis`; el puntero se acota a la salida.
- Foco-sigue-ratón: `window_at` hace el test de impacto (flotantes
  sobre teseladas, contra el rectángulo real de la superficie) y, al
  cambiar de ventana, emite `BodyEvent::PointerEntered`.
- `surface_px_size` en main.rs — tamaño presentado de una superficie,
  reusado por el test de impacto.

Compila + clippy limpio; pendiente de verificar en hardware.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-21 03:24:14 +00:00
parent 5230d42b11
commit b4ddab9c06
5 changed files with 255 additions and 56 deletions
+17
View File
@@ -32,6 +32,7 @@ use smithay::backend::renderer::utils::{
use smithay::backend::renderer::{Color32F, Frame, Renderer};
use smithay::backend::winit::{self, WinitEvent};
use smithay::input::keyboard::{xkb, FilterResult, KeyboardHandle, Keysym, ModifiersState};
use smithay::input::pointer::PointerHandle;
use smithay::input::{Seat, SeatHandler, SeatState};
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
use smithay::reexports::wayland_server::backend::{ClientData, ClientId, DisconnectReason};
@@ -107,6 +108,9 @@ struct App {
data_device_state: DataDeviceState,
seat: Seat<Self>,
keyboard: Option<KeyboardHandle<Self>>,
pointer: Option<PointerHandle<Self>>,
/// Posición del puntero en coordenadas globales.
pointer_loc: (f64, f64),
/// Ventanas gestionadas, en orden de aparición.
windows: Vec<ManagedWindow>,
@@ -534,6 +538,16 @@ fn render_loc(w: &ManagedWindow) -> (i32, i32) {
}
}
/// El tamaño en píxeles de la superficie de una ventana, si el cliente
/// ya presentó un buffer. `None` mientras no haya dibujado nada — la usa
/// el backend DRM para acertar el rectángulo en el test de impacto del
/// puntero.
fn surface_px_size(w: &ManagedWindow) -> Option<(i32, i32)> {
with_renderer_surface_state(&w.surface, |s| s.surface_size())
.flatten()
.map(|s| (s.w, s.h))
}
/// Carga las reglas de ventana del usuario, o ninguna si no hay archivo.
fn load_user_rules() -> Rules {
match Rules::default_path() {
@@ -627,6 +641,8 @@ fn build_app() -> Result<Setup, Box<dyn std::error::Error>> {
data_device_state: DataDeviceState::new::<App>(&dh),
seat,
keyboard: None,
pointer: None,
pointer_loc: (0.0, 0.0),
windows: Vec::new(),
body: BodyState::new(),
brain,
@@ -638,6 +654,7 @@ fn build_app() -> Result<Setup, Box<dyn std::error::Error>> {
let keyboard = app.seat.add_keyboard(Default::default(), 200, 25)?;
app.keyboard = Some(keyboard);
app.pointer = Some(app.seat.add_pointer());
// En modo embebido, el propio Desktop dicta los atajos a interceptar.
if let Brain::Embedded(desktop) = &app.brain {